3

As a variation of How can I convert a float to string?, I'm looking for a simple way to obtain a string representation of a float that is both concise and lossless. For instance:

let a = 1.0;
let b = 1.1234567890123456789012345678901e50;
let c = 1.1234567890123456789012345678901e-50;
for x in &[a, b, c] {
    println!("{}", x);
    println!("{:?}", x);
    println!("{}", x.to_string());
    println!("{}", f64::to_string(&x));
    println!("{:e}", x);
}

This produces:

1
1.0
1
1
1e0
112345678901234570000000000000000000000000000000000
112345678901234570000000000000000000000000000000000.0
112345678901234570000000000000000000000000000000000
112345678901234570000000000000000000000000000000000
1.1234567890123457e50
0.000000000000000000000000000000000000000000000000011234567890123456
0.000000000000000000000000000000000000000000000000011234567890123456
0.000000000000000000000000000000000000000000000000011234567890123456
0.000000000000000000000000000000000000000000000000011234567890123456
1.1234567890123456e-50

In other words:

  • {}, {:?}, x.to_string(), and f64::to_string(&x) all produce many leading/trailing zeros.
  • {:e} helps for printing b and c, but it enforces the exponential notation on all numbers, which results in unusual representations like 1e0.

Is there a way to produce a string that is lossless and uses exponential notation automatically only when it is appropriate?

Can such a conversion be extended generically to all numeric types?


To clarify the goal. I basically want the same how stringifying floats works in other programming language that smartly change from fixed point to exponential notation to produce an exact, human-readable representation. For instance on the JVM:

scala> (1.1234567890123456789012345678901e50).toString()
res1: String = 1.1234567890123457E50

scala> (1.0).toString()
res2: String = 1.0

scala> (1.1234567890123456789012345678901e-50).toString()
res3: String = 1.1234567890123456E-50
bluenote10
  • 23,414
  • 14
  • 122
  • 178
  • 2
    `"{}"` uses `Display`, as does `x.to_string()`, which just finds `f64::to_string`, so those three things are really all the same thing. – trent Jan 17 '20 at 20:09
  • 1
    [`f64::to_be_bytes`](https://doc.rust-lang.org/std/primitive.f64.html#method.to_be_bytes) + [Show u8 slice in hex representation](https://stackoverflow.com/q/27650312/155423) – Shepmaster Jan 17 '20 at 21:03
  • @Shepmaster I shouldn't have said "compact", maybe the better word is "human-friendly". For instance a human would most likely prefer `1` over `1e0` and a `e50` suffix instead of spelling out lots of zeros. Is there a better term in CS for such a "human normalization"? – bluenote10 Jan 17 '20 at 21:32
  • float must be manipulate like real in physic, first thing you learn is to calculate signifiant numbers, float do not differ that your job to know what you manipulate and so to know what you can trust. If your calcul allow 5 number precision show 5 number precisions, etc. – Stargateur Jan 18 '20 at 06:28

1 Answers1

2

The requirement here is a little bit vague. You may want to try ryu crate to see if it fits your needs:

use ryu;  // ryu = "1.0"

fn main() {
    let mut buffer = ryu::Buffer::new();
    let a = 1.0;
    let b = 1.1234567890123456789012345678901e50;
    let c = 1.1234567890123456789012345678901e-50;
    for &x in [a, b, c].iter() {
        println!("{}", buffer.format(x));
    }
}

It produces the output:

1.0
1.1234567890123457e50
1.1234567890123456e-50
edwardw
  • 12,652
  • 3
  • 40
  • 51