Nice floating point number formatting

#1

Python can do it.

``````>>> 10.0
10.0
>>> (10 ** -20)
1e-20
>>> (10 ** -20) * (1 + 2**-52)
1.0000000000000001e-20
``````

``````λ> 10 :: Double
10.0
λ> (10 ** (-20))
1.0e-20
λ> (10 ** (-20)) * (1 + (2 ** (-52)))
1.0000000000000001e-20
``````

Why can’t rust do it?

(code)

``````   {} -   "10" -   "0.00000000000000000001" - "0.000000000000000000010000000000000001"
{:?} -   "10" -   "0.00000000000000000001" - "0.000000000000000000010000000000000001"
{:e} -  "1e1" -                    "1e-20" -                 "1.0000000000000001e-20"
``````

I’m talking about automatic switching between exponential and non-exponential form based on magnitude; printing numbers fit for human consumption. The default format is so silly that you’re basically required to use exponential format in any sort of general context; but then I end up with those occassional late nights where I see `5e0` and it takes me a couple of seconds to remember whether this is larger than or less than 1.

That’s just silly!

…and yes, I know what you’re about to say, and I’ve already tried exactly that. A wrapper type doesn’t cut it. You can’t wrap every element of every `Vec` and `MyOneOffStruct` that you ever need to debug.

Nice should be the default.

How to print floating points with *limited* precision? ideally with %g semantics
#2

In C’s `printf`, this would be the `%g` format.

``````   g, G   The double argument is converted in style f or e (or F or E
for G conversions).  The precision specifies the number of
significant digits.  If the precision is missing, 6 digits are
given; if the precision is zero, it is treated as 1.  Style e
is used if the exponent from its conversion is less than -4 or
greater than or equal to the precision.  Trailing zeros are
removed from the fractional part of the result; a decimal
point appears only if it is followed by at least one digit.``````

#3

It’s close (and my original title was “General floating number point formatting”, after what I assume “g” stands for), though my preference is to default to minimal round-trip precision like our existing representations do (as well as the defaults in Haskell and Python).

#4

I have never had a case where `"{}"` or `"{:e}"` for a float was the formatting I wanted. I have always had to work around it.

In serde_json I have been using `dtoa` to get reasonable printing of floats. The API is somewhat less convenient but it is faster for serde_json’s use case than std::fmt, and it seems to use the same formatting as Python in your examples. [playground]

``````extern crate dtoa;

use std::{f64, io};

fn print_f64(f: f64) {
dtoa::write(io::stdout(), f).unwrap();
println!();
}

fn main() {
print_f64(10f64);
print_f64(10f64.powi(-20));
print_f64(10f64.powi(-20) * (1f64 + f64::EPSILON));
}
``````
``````10.0
1e-20
1.0000000000000001e-20
``````

#5

Lacking a `%g` equivalent is frustrating. There doesn’t even seem to be a good crate for this (@dtolnay’s does not appear to support displaying a particular number of significant digits). I pretty much only ever want to print a float with a specific number of significant digits, and rust does not seem to make this possible short of calling out to libc.

#6

I’ve hand-rolled something like this when I’ve needed to set a minimum number of significant digits before: https://play.rust-lang.org/?gist=5a253812c41dd51b3bc9c1f478e1ebde&version=stable

#7

The `%g` format specifier for `printf` is different from what current Python does. Python produces a short string which converts back to the original value. `%g` is lossy. But then, C does not require that string-to-floating-point is exact, either, so C isn’t really a good model here (Java is much better in this regard, as far as languages with a separate specification go).