Commit 51a9a6aab6

Andrew Kelley <andrew@ziglang.org>
2025-07-09 23:54:54
std: replace formatInteger with formatNumber
1 parent 5bc95a6
Changed files (3)
lib
std
lib/std/io/Writer.zig
@@ -744,11 +744,8 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void {
     switch (@typeInfo(T)) {
         .pointer => |info| {
             try w.writeAll(@typeName(info.child) ++ "@");
-            if (info.size == .slice)
-                try w.printInt(@intFromPtr(value.ptr), 16, .lower, .{})
-            else
-                try w.printInt(@intFromPtr(value), 16, .lower, .{});
-            return;
+            const int = if (info.size == .slice) @intFromPtr(value.ptr) else @intFromPtr(value);
+            return w.printInt(int, 16, .lower, .{});
         },
         .optional => |info| {
             if (@typeInfo(info.child) == .pointer) {
@@ -777,9 +774,9 @@ pub fn printValue(
             '*' => return w.printAddress(value),
             'f' => return value.format(w),
             'd' => switch (@typeInfo(T)) {
-                .float, .comptime_float => return printFloat(w, value, .decimal, options),
+                .float, .comptime_float => return printFloat(w, value, options.toNumber(.decimal, .lower)),
                 .int, .comptime_int => return printInt(w, value, 10, .lower, options),
-                .@"struct" => return value.formatInteger(w, 10, .lower),
+                .@"struct" => return value.formatNumber(w, options.toNumber(.decimal, .lower)),
                 .@"enum" => return printInt(w, @intFromEnum(value), 10, .lower, options),
                 .vector => return printVector(w, fmt, options, value, max_depth),
                 else => invalidFmtError(fmt, value),
@@ -789,22 +786,22 @@ pub fn printValue(
             'b' => switch (@typeInfo(T)) {
                 .int, .comptime_int => return printInt(w, value, 2, .lower, options),
                 .@"enum" => return printInt(w, @intFromEnum(value), 2, .lower, options),
-                .@"struct" => return value.formatInteger(w, 2, .lower),
+                .@"struct" => return value.formatNumber(w, options.toNumber(.binary, .lower)),
                 .vector => return printVector(w, fmt, options, value, max_depth),
                 else => invalidFmtError(fmt, value),
             },
             'o' => switch (@typeInfo(T)) {
                 .int, .comptime_int => return printInt(w, value, 8, .lower, options),
                 .@"enum" => return printInt(w, @intFromEnum(value), 8, .lower, options),
-                .@"struct" => return value.formatInteger(w, 8, .lower),
+                .@"struct" => return value.formatNumber(w, options.toNumber(.octal, .lower)),
                 .vector => return printVector(w, fmt, options, value, max_depth),
                 else => invalidFmtError(fmt, value),
             },
             'x' => switch (@typeInfo(T)) {
-                .float, .comptime_float => return printFloatHexOptions(w, value, .lower, options),
+                .float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .lower)),
                 .int, .comptime_int => return printInt(w, value, 16, .lower, options),
                 .@"enum" => return printInt(w, @intFromEnum(value), 16, .lower, options),
-                .@"struct" => return value.formatInteger(w, 16, .lower),
+                .@"struct" => return value.formatNumber(w, options.toNumber(.hex, .lower)),
                 .pointer => |info| switch (info.size) {
                     .one, .slice => {
                         const slice: []const u8 = value;
@@ -823,10 +820,10 @@ pub fn printValue(
                 else => invalidFmtError(fmt, value),
             },
             'X' => switch (@typeInfo(T)) {
-                .float, .comptime_float => return printFloatHexOptions(w, value, .lower, options),
+                .float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .lower)),
                 .int, .comptime_int => return printInt(w, value, 16, .upper, options),
                 .@"enum" => return printInt(w, @intFromEnum(value), 16, .upper, options),
-                .@"struct" => return value.formatInteger(w, 16, .upper),
+                .@"struct" => return value.formatNumber(w, options.toNumber(.hex, .upper)),
                 .pointer => |info| switch (info.size) {
                     .one, .slice => {
                         const slice: []const u8 = value;
@@ -872,8 +869,13 @@ pub fn printValue(
                 else => invalidFmtError(fmt, value),
             },
             'e' => switch (@typeInfo(T)) {
-                .float, .comptime_float => return printFloat(w, value, .scientific, options),
-                .@"struct" => return value.formatFloat(w, .scientific),
+                .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .lower)),
+                .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .lower)),
+                else => invalidFmtError(fmt, value),
+            },
+            'E' => switch (@typeInfo(T)) {
+                .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .upper)),
+                .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .upper)),
                 else => invalidFmtError(fmt, value),
             },
             't' => switch (@typeInfo(T)) {
@@ -923,7 +925,7 @@ pub fn printValue(
     switch (@typeInfo(T)) {
         .float, .comptime_float => {
             if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
-            return printFloat(w, value, .decimal, options);
+            return printFloat(w, value, options.toNumber(.decimal, .lower));
         },
         .int, .comptime_int => {
             if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
@@ -1262,12 +1264,13 @@ pub fn printUnicodeCodepoint(w: *Writer, c: u21) Error!void {
     return w.writeAll(buf[0..len]);
 }
 
-pub fn printFloat(
-    w: *Writer,
-    value: anytype,
-    mode: std.fmt.float.Mode,
-    options: std.fmt.Options,
-) Error!void {
+/// Uses a larger stack buffer; asserts mode is decimal or scientific.
+pub fn printFloat(w: *Writer, value: anytype, options: std.fmt.Number) Error!void {
+    const mode: std.fmt.float.Mode = switch (options.mode) {
+        .decimal => .decimal,
+        .scientific => .scientific,
+        .binary, .octal, .hex => unreachable,
+    };
     var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined;
     const s = std.fmt.float.render(&buf, value, .{
         .mode = mode,
@@ -1275,20 +1278,36 @@ pub fn printFloat(
     }) catch |err| switch (err) {
         error.BufferTooSmall => "(float)",
     };
-    return w.alignBufferOptions(s, options);
+    return w.alignBuffer(s, options.width orelse s.len, options.alignment, options.fill);
 }
 
-pub fn printFloatHexOptions(w: *Writer, value: anytype, case: std.fmt.Case, options: std.fmt.Options) Error!void {
+/// Uses a smaller stack buffer; asserts mode is not decimal or scientific.
+pub fn printFloatHexOptions(w: *Writer, value: anytype, options: std.fmt.Number) Error!void {
     var buf: [50]u8 = undefined; // for aligning
     var sub_writer: Writer = .fixed(&buf);
-    printFloatHex(&sub_writer, value, case, options.precision) catch unreachable; // buf is large enough
-    return w.alignBufferOptions(sub_writer.buffered(), options);
+    switch (options.mode) {
+        .decimal => unreachable,
+        .scientific => unreachable,
+        .binary => @panic("TODO"),
+        .octal => @panic("TODO"),
+        .hex => {},
+    }
+    printFloatHex(&sub_writer, value, options.case, options.precision) catch unreachable; // buf is large enough
+
+    const printed = sub_writer.buffered();
+    return w.alignBuffer(printed, options.width orelse printed.len, options.alignment, options.fill);
 }
 
 pub fn printFloatHex(w: *Writer, value: anytype, case: std.fmt.Case, opt_precision: ?usize) Error!void {
     if (std.math.signbit(value)) try w.writeByte('-');
-    if (std.math.isNan(value)) return w.writeAll("nan");
-    if (std.math.isInf(value)) return w.writeAll("inf");
+    if (std.math.isNan(value)) return w.writeAll(switch (case) {
+        .lower => "nan",
+        .upper => "NAN",
+    });
+    if (std.math.isInf(value)) return w.writeAll(switch (case) {
+        .lower => "inf",
+        .upper => "INF",
+    });
 
     const T = @TypeOf(value);
     const TU = std.meta.Int(.unsigned, @bitSizeOf(T));
@@ -1822,7 +1841,7 @@ test printInt {
 test "printFloat with comptime_float" {
     var buf: [20]u8 = undefined;
     var w: Writer = .fixed(&buf);
-    try w.printFloat(@as(comptime_float, 1.0), .scientific, .{});
+    try w.printFloat(@as(comptime_float, 1.0), std.fmt.Options.toNumber(.{}, .scientific, .lower));
     try std.testing.expectEqualStrings(w.buffered(), "1e0");
     try std.testing.expectFmt("1", "{}", .{1.0});
 }
lib/std/math/big/int.zig
@@ -2030,11 +2030,11 @@ pub const Mutable = struct {
     }
 
     pub fn format(self: Mutable, w: *std.io.Writer) std.io.Writer.Error!void {
-        return formatInteger(self, w, 10, .lower);
+        return formatNumber(self, w, .{});
     }
 
-    pub fn formatInteger(self: Const, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void {
-        return self.toConst().formatInteger(w, base, case);
+    pub fn formatNumber(self: Const, w: *std.io.Writer, n: std.fmt.Number) std.io.Writer.Error!void {
+        return self.toConst().formatNumber(w, n);
     }
 };
 
@@ -2329,7 +2329,7 @@ pub const Const = struct {
     /// this function will fail to print the string, printing "(BigInt)" instead of a number.
     /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
     /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
-    pub fn formatInteger(self: Const, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void {
+    pub fn formatNumber(self: Const, w: *std.io.Writer, number: std.fmt.Number) std.io.Writer.Error!void {
         const available_len = 64;
         if (self.limbs.len > available_len)
             return w.writeAll("(BigInt)");
@@ -2341,7 +2341,8 @@ pub const Const = struct {
             .positive = false,
         };
         var buf: [biggest.sizeInBaseUpperBound(2)]u8 = undefined;
-        const len = self.toString(&buf, base, case, &limbs);
+        const base: u8 = number.mode.base() orelse @panic("TODO print big int in scientific form");
+        const len = self.toString(&buf, base, number.case, &limbs);
         return w.writeAll(buf[0..len]);
     }
 
@@ -2913,15 +2914,15 @@ pub const Managed = struct {
 
     /// To allow `std.fmt.format` to work with `Managed`.
     pub fn format(self: Managed, w: *std.io.Writer) std.io.Writer.Error!void {
-        return formatInteger(self, w, 10, .lower);
+        return formatNumber(self, w, .{});
     }
 
     /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`,
     /// this function will fail to print the string, printing "(BigInt)" instead of a number.
     /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
     /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
-    pub fn formatInteger(self: Managed, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void {
-        return self.toConst().formatInteger(w, base, case);
+    pub fn formatNumber(self: Managed, w: *std.io.Writer, n: std.fmt.Number) std.io.Writer.Error!void {
+        return self.toConst().formatNumber(w, n);
     }
 
     /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==
lib/std/fmt.zig
@@ -37,6 +37,45 @@ pub const Options = struct {
     width: ?usize = null,
     alignment: Alignment = default_alignment,
     fill: u8 = default_fill_char,
+
+    pub fn toNumber(o: Options, mode: Number.Mode, case: Case) Number {
+        return .{
+            .mode = mode,
+            .case = case,
+            .precision = o.precision,
+            .width = o.width,
+            .alignment = o.alignment,
+            .fill = o.fill,
+        };
+    }
+};
+
+pub const Number = struct {
+    mode: Mode = .decimal,
+    /// Affects hex digits as well as floating point "inf"/"INF".
+    case: Case = .lower,
+    precision: ?usize = null,
+    width: ?usize = null,
+    alignment: Alignment = default_alignment,
+    fill: u8 = default_fill_char,
+
+    pub const Mode = enum {
+        decimal,
+        binary,
+        octal,
+        hex,
+        scientific,
+
+        pub fn base(mode: Mode) ?u8 {
+            return switch (mode) {
+                .decimal => 10,
+                .binary => 2,
+                .octal => 8,
+                .hex => 16,
+                .scientific => null,
+            };
+        }
+    };
 };
 
 /// Renders fmt string with args, calling `writer` with slices of bytes.