# Control float/double formatting with \`{any}\`

**URL:** <https://ziggit.dev/t/control-float-double-formatting-with-any/3838>\
**Category:** Help\
**Tags:** language\
**Created:** [April 5, 2024, 4:52pm UTC](https://ziggit.dev/t/control-float-double-formatting-with-any/3838 "2024-04-05T16:52:57Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Luke](https://ziggit.dev/letter_avatar_proxy/v4/letter/l/7ab992/32.png) [@Luke](https://ziggit.dev/u/Luke)\
**Post date:** [April 5, 2024, 4:52pm UTC](https://ziggit.dev/t/control-float-double-formatting-with-any/3838/1 "2024-04-05T16:52:57Z")

</div>

When trying to debug-print structs, like you would with `{:?}` in Rust, how to control the formatting of float/doubles when using `{any}`?:

```zig
const std = @import("std");

const Foo = struct {
    bar: f32,
};

pub fn main() void {
    const foo = Foo { .bar = 0.123 };
    std.log.debug("foo {any}", .{ foo });
}

```

For example, trying to use `decimal` formatting instead of `scientific` ?

---

<div class="post-metadata">

**Author:** ![dude\_the\_builder](https://ziggit.dev/user_avatar/ziggit.dev/dude_the_builder/32/557_2.png) [@dude\_the\_builder](https://ziggit.dev/u/dude_the_builder)\
**Post date:** [April 5, 2024, 5:48pm UTC](https://ziggit.dev/t/control-float-double-formatting-with-any/3838/2 "2024-04-05T17:48:46Z")

</div>

`{any}` will output a value of any type using a _default_ format, as per [fmt](https://github.com/ziglang/zig/blob/e5d900268a1d969dcaf4f2d657e96a5a5f217e7b/lib/std/fmt.zig#L30) doc. To format a numeric value as a decimal use `{d}`. You can add width and precision: `{d:6.2}`.

---

<div class="post-metadata">

**Author:** ![Luke](https://ziggit.dev/letter_avatar_proxy/v4/letter/l/7ab992/32.png) [@Luke](https://ziggit.dev/u/Luke)\
**Post date:** [April 5, 2024, 5:51pm UTC](https://ziggit.dev/t/control-float-double-formatting-with-any/3838/3 "2024-04-05T17:51:26Z")

</div>

So I take from your post that the answer to the question is that this is not possible to configure the formatting of float/double when using `{any}`.

---

<div class="post-metadata">

**Author:** ![Sze](https://ziggit.dev/user_avatar/ziggit.dev/sze/32/496_2.png) [@Sze](https://ziggit.dev/u/Sze)\
**Post date:** [April 5, 2024, 10:30pm UTC](https://ziggit.dev/t/control-float-double-formatting-with-any/3838/4 "2024-04-05T22:30:30Z")

</div>

As explained here:

> [@Idiomatic Complex Formatting](https://ziggit.dev/t/idiomatic-complex-formatting/3735/5):
>
> The idiomatic way of implementing custom formatting would be to implement a format function. [std.fmt.format](https://ziglang.org/documentation/master/std/#std.fmt.format), which std.debug.print and other formatting functions that take a format string with placeholders are derived from, has this to say in its doc comment: If a formatted user type contains a function of the type pub fn format( value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype, ) !void with ? being the type formatted, this function will be call…

You can add a `format` function to your type `Foo` or you could use the [`std.fmt.Formatter`](https://ziglang.org/documentation/master/std/#std.fmt.Formatter) and use a freestanding function to explicitly format an argument to a printing function.

---

<div class="post-metadata">

**Author:** ![Luke](https://ziggit.dev/letter_avatar_proxy/v4/letter/l/7ab992/32.png) [@Luke](https://ziggit.dev/u/Luke)\
**Post date:** [April 6, 2024, 7:11am UTC](https://ziggit.dev/t/control-float-double-formatting-with-any/3838/5 "2024-04-06T07:11:55Z")

</div>

Yes, thanks for that I forgot to mention it, but I mainly use `{any}` for debug purposes (the way I use `Debug` and `{:?}` in rust) and I can’t read scientific notation due to my smol brain.

Having to write a format function to any random struct I want to debug is just not practical.

I though that zig would have something equivalent to `std::fixed` to globally configure the way floats are formatted.

> You can add a `format` function to your type `Foo` or you could use the [`std.fmt.Formatter`](https://ziglang.org/documentation/master/std/#std.fmt.Formatter) and use a freestanding function to explicitly format an argument to a printing function.

Ok I didn’t know this. So you could theorically re-implement `{any}` with your own generic Formatter and use it to debug-print any struct with the formatting you want.

I tried this:

```zig
fn formatDebugCtr(comptime T: type) (fn (T, []const u8, std.fmt.FormatOptions, anytype) anyerror!void) {
  return struct {
    fn formatDebug(
      any: T,
      comptime fmt: []const u8,
      options: std.fmt.FormatOptions,
      writer: anytype,
    ) !void {
      // TODO: set options to decimal?
      try std.fmt.formatType(any, fmt, options, writer, std.options.fmt_max_depth);
    }
  }.formatDebug;
}

pub fn fmtDebug(any: anytype) std.fmt.Formatter(formatDebugCtr(@TypeOf(any))) {
  return .{ .data = any };
}

```

But:

1. I realize that FormatOptions does not let you specify the float format (scientific/decimal) only the precision.
2. This crashes the compiler with `zig compiler bug: GenericPoison`.

So I’am out of options for a reasonably simple solution unfortunately.

---

<div class="post-metadata">

**Author:** ![Sze](https://ziggit.dev/user_avatar/ziggit.dev/sze/32/496_2.png) [@Sze](https://ziggit.dev/u/Sze)\
**Post date:** [April 6, 2024, 7:59am UTC](https://ziggit.dev/t/control-float-double-formatting-with-any/3838/6 "2024-04-06T07:59:05Z")

</div>

You can just do `try writer.print("{d}", .{value});` within `formatDebug` if `T` is a number type. You can just ignore the format options if you want to.

Also you could write your own wrapper around `std.debug.print` or whatever you are using and automatically apply that formatting overwrite to any of the tuple arguments which are numbers.

For composite types it is more complicated and maybe too much work.

You also could maybe locally hack your std library and just change your default formatting. Maybe you could even copy the standard library formatting code and change it and use it as a custom library, but I don’t know whether it would be relatively straightforward or not.

Regarding 2. I am not entirely sure but maybe you are creating an endless loop, because I guess `formatTypes` with the same arguments just calls the `formatDebug` function again.

* * *

Hmm I guess you are calling it on the inner type, so it should use the default formatting? I am not sure what is going on there. What zig version are you running, do you have a small complete example that creates the compiler bug?

---

<div class="post-metadata">

**Author:** ![Luke](https://ziggit.dev/letter_avatar_proxy/v4/letter/l/7ab992/32.png) [@Luke](https://ziggit.dev/u/Luke)\
**Post date:** [April 6, 2024, 8:17am UTC](https://ziggit.dev/t/control-float-double-formatting-with-any/3838/7 "2024-04-06T08:17:23Z")

</div>

> You can just do `try writer.print("{d}", .{value});` within `formatDebug` if `T` is a number type. You can just ignore the format options if you want to.

> Also you could write your own wrapper around `std.debug.print` or whatever you are using and automatically apply that formatting overwrite to any of the tuple arguments which are numbers.

Ok so from @dude_the_builder’s and this statement I realize that I wasn’t clear in my initial question. I have updated it with, hopefully, a clearer intent.

I am trying to debug print arbitrary complex structs which will contain some float fields (among many other fields which might be struct themselves) and I wanted to use decimal notation for those float fields.

> For composite types it is more complicated and maybe too much work.

Yes that’s my point. Impossible to do today without a significant effort.

> You also could maybe locally hack your std library and just change your default formatting. Maybe you could even copy the standard library formatting code and change it and use it as a custom library, but I don’t know whether it would be relatively straightforward or not.

Hacking it wouldn’t but having to keep it up to date with upstream zig would.

> Hmm I guess you are calling it on the inner type, so it should use the default formatting? I am not sure what is going on there. What zig version are you running, do you have a small complete example that creates the compiler bug?

```bash
$ zig version
0.12.0-dev.3533+e5d900268

```

MRE:

```zig
const std = @import("std");

fn formatDebugCtr(comptime T: type) (fn (T, []const u8, std.fmt.FormatOptions, anytype) anyerror!void) {
  return struct {
    fn formatDebug(
      any: T,
      comptime fmt: []const u8,
      options: std.fmt.FormatOptions,
      writer: anytype,
    ) !void {
      // TODO: set options to decimal?
      try std.fmt.formatType(any, fmt, options, writer, std.options.fmt_max_depth);
    }
  }.formatDebug;
}

pub fn fmtDebug(any: anytype) std.fmt.Formatter(formatDebugCtr(@TypeOf(any))) {
  return .{ .data = any };
}

const MyStruct = struct {
  foo: f32,
};

pub fn main() !void {
  const myStruct: MyStruct = .{ .foo = 0.123 };
  std.log.debug("{}", .{ fmtDebug(myStruct) });
}

```

compile it with:

```bash
$ zig build-exe mre.zig
thread 41603 panic: zig compiler bug: GenericPoison
Unable to dump stack trace: debug info stripped
Aborted (core dumped)

```

---

<div class="post-metadata">

**Author:** ![Sze](https://ziggit.dev/user_avatar/ziggit.dev/sze/32/496_2.png) [@Sze](https://ziggit.dev/u/Sze)\
**Post date:** [April 7, 2024, 7:36am UTC](https://ziggit.dev/t/control-float-double-formatting-with-any/3838/8 "2024-04-07T07:36:50Z")

</div>

Regarding the compiler bug I don’t know whether this is meant to be something that isn’t allowed and should get some kind of error message, about going a few levels too deep into meta-programming or something along those lines, or whether this is something that actually should work in zig.

It would be good to get a perspective on this compiler bug from somebody who knows what is going on. @mlugg Can you give us some perspective on what is happening here, if this is something that needs to be fixed / has an issue or needs one?

* * *

I can change the code to mimic what `Formatter` does and then it works (I think because it avoids using the “generically instanced” function and instead generates a type with a `format` function):

```zig
const std = @import("std");

fn FormatIt(comptime T: type) type {
    return struct {
        data: T,

        pub fn format(
            self: @This(),
            comptime fmt: []const u8,
            options: std.fmt.FormatOptions,
            writer: anytype,
        ) !void {
            try std.fmt.formatType(self.data, fmt, options, writer, std.options.fmt_max_depth);
        }
    };
}

fn formatIt(it: anytype) FormatIt(@TypeOf(it)) {
    return .{ .data = it };
}

const MyStruct = struct {
    foo: f32,
};

pub fn main() !void {
    const myStruct: MyStruct = .{ .foo = 0.123 };
    std.log.debug("{}", .{formatIt(myStruct)});
}

```

Output:

```zig
debug: mre2.MyStruct{ .foo = 1.23000003e-01 }

```

The part that is still unsolved is how could you customize the default way of printing nested values.

Maybe there could be something similar to a custom logger but for formatting defaults, which would allow you to pick which formatting is used when no explicit one is provided, but I don’t know if something like this has been proposed/rejected already.

---

<div class="post-metadata">

**Author:** ![permutationlock](https://ziggit.dev/user_avatar/ziggit.dev/permutationlock/32/9958_2.png) [@permutationlock](https://ziggit.dev/u/permutationlock)\
**Post date:** [April 7, 2024, 8:33am UTC](https://ziggit.dev/t/control-float-double-formatting-with-any/3838/9 "2024-04-07T08:33:36Z")

</div>

I think it is the return type on `formatDebugCtr` that is causing the compiler to have a headache here. I’m not knowledgeable on compiler internals and I haven’t been using Zig recently, but I remember running into similar issues when playing around with generic madness in the past.

Switching to the following compiles without issue.

```zig
const std = @import("std");

fn FormatDebugCtr(comptime T: type) type {
  return struct {
    fn formatDebug(
      any: T,
      comptime fmt: []const u8,
      options: std.fmt.FormatOptions,
      writer: anytype,
    ) !void {
      // TODO: set options to decimal?
      try std.fmt.formatType(any, fmt, options, writer, std.options.fmt_max_depth);
    }
  };
}

pub fn fmtDebug(any: anytype) std.fmt.Formatter(FormatDebugCtr(@TypeOf(any)).formatDebug) {
  return .{ .data = any };
}

const MyStruct = struct {
  foo: f32,
};

pub fn main() !void {
  const myStruct: MyStruct = .{ .foo = 0.123 };
  std.log.debug("{}", .{ fmtDebug(myStruct) });
}

```

---

<div class="post-metadata">

**Author:** ![mlugg](https://ziggit.dev/user_avatar/ziggit.dev/mlugg/32/437_2.png) [@mlugg](https://ziggit.dev/u/mlugg)\
**Post date:** [April 7, 2024, 4:39pm UTC](https://ziggit.dev/t/control-float-double-formatting-with-any/3838/10 "2024-04-07T16:39:45Z")

</div>

Oh man, generic poison leaks are always annoying. This _might_ be one of [these issues](https://github.com/ziglang/zig/issues?q=is%3Aissue+is%3Aopen+GenericPoison), but I couldn’t be sure without looking into it more deeply. If you have a simple repro (it looks like you do), I’d suggest opening an issue; if it is the same as another issue we’ll catch it and close both.

---

<div class="post-metadata">

**Author:** ![Luke](https://ziggit.dev/letter_avatar_proxy/v4/letter/l/7ab992/32.png) [@Luke](https://ziggit.dev/u/Luke)\
**Post date:** [April 8, 2024, 10:24am UTC](https://ziggit.dev/t/control-float-double-formatting-with-any/3838/11 "2024-04-08T10:24:25Z")

</div>

I think [this issue](https://github.com/ziglang/zig/issues/17078) already tracks the same problem.

At least it crashes at the same stack trace in gdb. I would err on the side of not adding more noise to the issue tracker. Let me know if you thing otherwise.
