authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-09 14:54:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-09 14:54:54-07:00
log51a9a6aab696b9fbd69dadb5a569cfe184fabecc
tree737829297856590eda0d1f93141d7fb4e971f2ae
parent5bc95a6fa2af86689c37a87a5899e7e34d6084ee

std: replace formatInteger with formatNumber


3 files changed, 96 insertions(+), 37 deletions(-)

lib/std/fmt.zig+39
...@@ -37,6 +37,45 @@ pub const Options = struct {...@@ -37,6 +37,45 @@ pub const Options = struct {
37 width: ?usize = null,37 width: ?usize = null,
38 alignment: Alignment = default_alignment,38 alignment: Alignment = default_alignment,
39 fill: u8 = default_fill_char,39 fill: u8 = default_fill_char,
40
41 pub fn toNumber(o: Options, mode: Number.Mode, case: Case) Number {
42 return .{
43 .mode = mode,
44 .case = case,
45 .precision = o.precision,
46 .width = o.width,
47 .alignment = o.alignment,
48 .fill = o.fill,
49 };
50 }
51};
52
53pub const Number = struct {
54 mode: Mode = .decimal,
55 /// Affects hex digits as well as floating point "inf"/"INF".
56 case: Case = .lower,
57 precision: ?usize = null,
58 width: ?usize = null,
59 alignment: Alignment = default_alignment,
60 fill: u8 = default_fill_char,
61
62 pub const Mode = enum {
63 decimal,
64 binary,
65 octal,
66 hex,
67 scientific,
68
69 pub fn base(mode: Mode) ?u8 {
70 return switch (mode) {
71 .decimal => 10,
72 .binary => 2,
73 .octal => 8,
74 .hex => 16,
75 .scientific => null,
76 };
77 }
78 };
40};79};
4180
42/// Renders fmt string with args, calling `writer` with slices of bytes.81/// Renders fmt string with args, calling `writer` with slices of bytes.
lib/std/io/Writer.zig+48-29
...@@ -744,11 +744,8 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void {...@@ -744,11 +744,8 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void {
744 switch (@typeInfo(T)) {744 switch (@typeInfo(T)) {
745 .pointer => |info| {745 .pointer => |info| {
746 try w.writeAll(@typeName(info.child) ++ "@");746 try w.writeAll(@typeName(info.child) ++ "@");
747 if (info.size == .slice)747 const int = if (info.size == .slice) @intFromPtr(value.ptr) else @intFromPtr(value);
748 try w.printInt(@intFromPtr(value.ptr), 16, .lower, .{})748 return w.printInt(int, 16, .lower, .{});
749 else
750 try w.printInt(@intFromPtr(value), 16, .lower, .{});
751 return;
752 },749 },
753 .optional => |info| {750 .optional => |info| {
754 if (@typeInfo(info.child) == .pointer) {751 if (@typeInfo(info.child) == .pointer) {
...@@ -777,9 +774,9 @@ pub fn printValue(...@@ -777,9 +774,9 @@ pub fn printValue(
777 '*' => return w.printAddress(value),774 '*' => return w.printAddress(value),
778 'f' => return value.format(w),775 'f' => return value.format(w),
779 'd' => switch (@typeInfo(T)) {776 'd' => switch (@typeInfo(T)) {
780 .float, .comptime_float => return printFloat(w, value, .decimal, options),777 .float, .comptime_float => return printFloat(w, value, options.toNumber(.decimal, .lower)),
781 .int, .comptime_int => return printInt(w, value, 10, .lower, options),778 .int, .comptime_int => return printInt(w, value, 10, .lower, options),
782 .@"struct" => return value.formatInteger(w, 10, .lower),779 .@"struct" => return value.formatNumber(w, options.toNumber(.decimal, .lower)),
783 .@"enum" => return printInt(w, @intFromEnum(value), 10, .lower, options),780 .@"enum" => return printInt(w, @intFromEnum(value), 10, .lower, options),
784 .vector => return printVector(w, fmt, options, value, max_depth),781 .vector => return printVector(w, fmt, options, value, max_depth),
785 else => invalidFmtError(fmt, value),782 else => invalidFmtError(fmt, value),
...@@ -789,22 +786,22 @@ pub fn printValue(...@@ -789,22 +786,22 @@ pub fn printValue(
789 'b' => switch (@typeInfo(T)) {786 'b' => switch (@typeInfo(T)) {
790 .int, .comptime_int => return printInt(w, value, 2, .lower, options),787 .int, .comptime_int => return printInt(w, value, 2, .lower, options),
791 .@"enum" => return printInt(w, @intFromEnum(value), 2, .lower, options),788 .@"enum" => return printInt(w, @intFromEnum(value), 2, .lower, options),
792 .@"struct" => return value.formatInteger(w, 2, .lower),789 .@"struct" => return value.formatNumber(w, options.toNumber(.binary, .lower)),
793 .vector => return printVector(w, fmt, options, value, max_depth),790 .vector => return printVector(w, fmt, options, value, max_depth),
794 else => invalidFmtError(fmt, value),791 else => invalidFmtError(fmt, value),
795 },792 },
796 'o' => switch (@typeInfo(T)) {793 'o' => switch (@typeInfo(T)) {
797 .int, .comptime_int => return printInt(w, value, 8, .lower, options),794 .int, .comptime_int => return printInt(w, value, 8, .lower, options),
798 .@"enum" => return printInt(w, @intFromEnum(value), 8, .lower, options),795 .@"enum" => return printInt(w, @intFromEnum(value), 8, .lower, options),
799 .@"struct" => return value.formatInteger(w, 8, .lower),796 .@"struct" => return value.formatNumber(w, options.toNumber(.octal, .lower)),
800 .vector => return printVector(w, fmt, options, value, max_depth),797 .vector => return printVector(w, fmt, options, value, max_depth),
801 else => invalidFmtError(fmt, value),798 else => invalidFmtError(fmt, value),
802 },799 },
803 'x' => switch (@typeInfo(T)) {800 'x' => switch (@typeInfo(T)) {
804 .float, .comptime_float => return printFloatHexOptions(w, value, .lower, options),801 .float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .lower)),
805 .int, .comptime_int => return printInt(w, value, 16, .lower, options),802 .int, .comptime_int => return printInt(w, value, 16, .lower, options),
806 .@"enum" => return printInt(w, @intFromEnum(value), 16, .lower, options),803 .@"enum" => return printInt(w, @intFromEnum(value), 16, .lower, options),
807 .@"struct" => return value.formatInteger(w, 16, .lower),804 .@"struct" => return value.formatNumber(w, options.toNumber(.hex, .lower)),
808 .pointer => |info| switch (info.size) {805 .pointer => |info| switch (info.size) {
809 .one, .slice => {806 .one, .slice => {
810 const slice: []const u8 = value;807 const slice: []const u8 = value;
...@@ -823,10 +820,10 @@ pub fn printValue(...@@ -823,10 +820,10 @@ pub fn printValue(
823 else => invalidFmtError(fmt, value),820 else => invalidFmtError(fmt, value),
824 },821 },
825 'X' => switch (@typeInfo(T)) {822 'X' => switch (@typeInfo(T)) {
826 .float, .comptime_float => return printFloatHexOptions(w, value, .lower, options),823 .float, .comptime_float => return printFloatHexOptions(w, value, options.toNumber(.hex, .lower)),
827 .int, .comptime_int => return printInt(w, value, 16, .upper, options),824 .int, .comptime_int => return printInt(w, value, 16, .upper, options),
828 .@"enum" => return printInt(w, @intFromEnum(value), 16, .upper, options),825 .@"enum" => return printInt(w, @intFromEnum(value), 16, .upper, options),
829 .@"struct" => return value.formatInteger(w, 16, .upper),826 .@"struct" => return value.formatNumber(w, options.toNumber(.hex, .upper)),
830 .pointer => |info| switch (info.size) {827 .pointer => |info| switch (info.size) {
831 .one, .slice => {828 .one, .slice => {
832 const slice: []const u8 = value;829 const slice: []const u8 = value;
...@@ -872,8 +869,13 @@ pub fn printValue(...@@ -872,8 +869,13 @@ pub fn printValue(
872 else => invalidFmtError(fmt, value),869 else => invalidFmtError(fmt, value),
873 },870 },
874 'e' => switch (@typeInfo(T)) {871 'e' => switch (@typeInfo(T)) {
875 .float, .comptime_float => return printFloat(w, value, .scientific, options),872 .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .lower)),
876 .@"struct" => return value.formatFloat(w, .scientific),873 .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .lower)),
874 else => invalidFmtError(fmt, value),
875 },
876 'E' => switch (@typeInfo(T)) {
877 .float, .comptime_float => return printFloat(w, value, options.toNumber(.scientific, .upper)),
878 .@"struct" => return value.formatNumber(w, options.toNumber(.scientific, .upper)),
877 else => invalidFmtError(fmt, value),879 else => invalidFmtError(fmt, value),
878 },880 },
879 't' => switch (@typeInfo(T)) {881 't' => switch (@typeInfo(T)) {
...@@ -923,7 +925,7 @@ pub fn printValue(...@@ -923,7 +925,7 @@ pub fn printValue(
923 switch (@typeInfo(T)) {925 switch (@typeInfo(T)) {
924 .float, .comptime_float => {926 .float, .comptime_float => {
925 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);927 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
926 return printFloat(w, value, .decimal, options);928 return printFloat(w, value, options.toNumber(.decimal, .lower));
927 },929 },
928 .int, .comptime_int => {930 .int, .comptime_int => {
929 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);931 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
...@@ -1262,12 +1264,13 @@ pub fn printUnicodeCodepoint(w: *Writer, c: u21) Error!void {...@@ -1262,12 +1264,13 @@ pub fn printUnicodeCodepoint(w: *Writer, c: u21) Error!void {
1262 return w.writeAll(buf[0..len]);1264 return w.writeAll(buf[0..len]);
1263}1265}
12641266
1265pub fn printFloat(1267/// Uses a larger stack buffer; asserts mode is decimal or scientific.
1266 w: *Writer,1268pub fn printFloat(w: *Writer, value: anytype, options: std.fmt.Number) Error!void {
1267 value: anytype,1269 const mode: std.fmt.float.Mode = switch (options.mode) {
1268 mode: std.fmt.float.Mode,1270 .decimal => .decimal,
1269 options: std.fmt.Options,1271 .scientific => .scientific,
1270) Error!void {1272 .binary, .octal, .hex => unreachable,
1273 };
1271 var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined;1274 var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined;
1272 const s = std.fmt.float.render(&buf, value, .{1275 const s = std.fmt.float.render(&buf, value, .{
1273 .mode = mode,1276 .mode = mode,
...@@ -1275,20 +1278,36 @@ pub fn printFloat(...@@ -1275,20 +1278,36 @@ pub fn printFloat(
1275 }) catch |err| switch (err) {1278 }) catch |err| switch (err) {
1276 error.BufferTooSmall => "(float)",1279 error.BufferTooSmall => "(float)",
1277 };1280 };
1278 return w.alignBufferOptions(s, options);1281 return w.alignBuffer(s, options.width orelse s.len, options.alignment, options.fill);
1279}1282}
12801283
1281pub fn printFloatHexOptions(w: *Writer, value: anytype, case: std.fmt.Case, options: std.fmt.Options) Error!void {1284/// Uses a smaller stack buffer; asserts mode is not decimal or scientific.
1285pub fn printFloatHexOptions(w: *Writer, value: anytype, options: std.fmt.Number) Error!void {
1282 var buf: [50]u8 = undefined; // for aligning1286 var buf: [50]u8 = undefined; // for aligning
1283 var sub_writer: Writer = .fixed(&buf);1287 var sub_writer: Writer = .fixed(&buf);
1284 printFloatHex(&sub_writer, value, case, options.precision) catch unreachable; // buf is large enough1288 switch (options.mode) {
1285 return w.alignBufferOptions(sub_writer.buffered(), options);1289 .decimal => unreachable,
1290 .scientific => unreachable,
1291 .binary => @panic("TODO"),
1292 .octal => @panic("TODO"),
1293 .hex => {},
1294 }
1295 printFloatHex(&sub_writer, value, options.case, options.precision) catch unreachable; // buf is large enough
1296
1297 const printed = sub_writer.buffered();
1298 return w.alignBuffer(printed, options.width orelse printed.len, options.alignment, options.fill);
1286}1299}
12871300
1288pub fn printFloatHex(w: *Writer, value: anytype, case: std.fmt.Case, opt_precision: ?usize) Error!void {1301pub fn printFloatHex(w: *Writer, value: anytype, case: std.fmt.Case, opt_precision: ?usize) Error!void {
1289 if (std.math.signbit(value)) try w.writeByte('-');1302 if (std.math.signbit(value)) try w.writeByte('-');
1290 if (std.math.isNan(value)) return w.writeAll("nan");1303 if (std.math.isNan(value)) return w.writeAll(switch (case) {
1291 if (std.math.isInf(value)) return w.writeAll("inf");1304 .lower => "nan",
1305 .upper => "NAN",
1306 });
1307 if (std.math.isInf(value)) return w.writeAll(switch (case) {
1308 .lower => "inf",
1309 .upper => "INF",
1310 });
12921311
1293 const T = @TypeOf(value);1312 const T = @TypeOf(value);
1294 const TU = std.meta.Int(.unsigned, @bitSizeOf(T));1313 const TU = std.meta.Int(.unsigned, @bitSizeOf(T));
...@@ -1822,7 +1841,7 @@ test printInt {...@@ -1822,7 +1841,7 @@ test printInt {
1822test "printFloat with comptime_float" {1841test "printFloat with comptime_float" {
1823 var buf: [20]u8 = undefined;1842 var buf: [20]u8 = undefined;
1824 var w: Writer = .fixed(&buf);1843 var w: Writer = .fixed(&buf);
1825 try w.printFloat(@as(comptime_float, 1.0), .scientific, .{});1844 try w.printFloat(@as(comptime_float, 1.0), std.fmt.Options.toNumber(.{}, .scientific, .lower));
1826 try std.testing.expectEqualStrings(w.buffered(), "1e0");1845 try std.testing.expectEqualStrings(w.buffered(), "1e0");
1827 try std.testing.expectFmt("1", "{}", .{1.0});1846 try std.testing.expectFmt("1", "{}", .{1.0});
1828}1847}
lib/std/math/big/int.zig+9-8
...@@ -2030,11 +2030,11 @@ pub const Mutable = struct {...@@ -2030,11 +2030,11 @@ pub const Mutable = struct {
2030 }2030 }
20312031
2032 pub fn format(self: Mutable, w: *std.io.Writer) std.io.Writer.Error!void {2032 pub fn format(self: Mutable, w: *std.io.Writer) std.io.Writer.Error!void {
2033 return formatInteger(self, w, 10, .lower);2033 return formatNumber(self, w, .{});
2034 }2034 }
20352035
2036 pub fn formatInteger(self: Const, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void {2036 pub fn formatNumber(self: Const, w: *std.io.Writer, n: std.fmt.Number) std.io.Writer.Error!void {
2037 return self.toConst().formatInteger(w, base, case);2037 return self.toConst().formatNumber(w, n);
2038 }2038 }
2039};2039};
20402040
...@@ -2329,7 +2329,7 @@ pub const Const = struct {...@@ -2329,7 +2329,7 @@ pub const Const = struct {
2329 /// this function will fail to print the string, printing "(BigInt)" instead of a number.2329 /// this function will fail to print the string, printing "(BigInt)" instead of a number.
2330 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.2330 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
2331 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.2331 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
2332 pub fn formatInteger(self: Const, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void {2332 pub fn formatNumber(self: Const, w: *std.io.Writer, number: std.fmt.Number) std.io.Writer.Error!void {
2333 const available_len = 64;2333 const available_len = 64;
2334 if (self.limbs.len > available_len)2334 if (self.limbs.len > available_len)
2335 return w.writeAll("(BigInt)");2335 return w.writeAll("(BigInt)");
...@@ -2341,7 +2341,8 @@ pub const Const = struct {...@@ -2341,7 +2341,8 @@ pub const Const = struct {
2341 .positive = false,2341 .positive = false,
2342 };2342 };
2343 var buf: [biggest.sizeInBaseUpperBound(2)]u8 = undefined;2343 var buf: [biggest.sizeInBaseUpperBound(2)]u8 = undefined;
2344 const len = self.toString(&buf, base, case, &limbs);2344 const base: u8 = number.mode.base() orelse @panic("TODO print big int in scientific form");
2345 const len = self.toString(&buf, base, number.case, &limbs);
2345 return w.writeAll(buf[0..len]);2346 return w.writeAll(buf[0..len]);
2346 }2347 }
23472348
...@@ -2913,15 +2914,15 @@ pub const Managed = struct {...@@ -2913,15 +2914,15 @@ pub const Managed = struct {
29132914
2914 /// To allow `std.fmt.format` to work with `Managed`.2915 /// To allow `std.fmt.format` to work with `Managed`.
2915 pub fn format(self: Managed, w: *std.io.Writer) std.io.Writer.Error!void {2916 pub fn format(self: Managed, w: *std.io.Writer) std.io.Writer.Error!void {
2916 return formatInteger(self, w, 10, .lower);2917 return formatNumber(self, w, .{});
2917 }2918 }
29182919
2919 /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`,2920 /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`,
2920 /// this function will fail to print the string, printing "(BigInt)" instead of a number.2921 /// this function will fail to print the string, printing "(BigInt)" instead of a number.
2921 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.2922 /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory.
2922 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.2923 /// See `toString` and `toStringAlloc` for a way to print big integers without failure.
2923 pub fn formatInteger(self: Managed, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void {2924 pub fn formatNumber(self: Managed, w: *std.io.Writer, n: std.fmt.Number) std.io.Writer.Error!void {
2924 return self.toConst().formatInteger(w, base, case);2925 return self.toConst().formatNumber(w, n);
2925 }2926 }
29262927
2927 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==2928 /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==