2

I've been working on some Rust projects lately to learn the language and have some fun. I am writing something similar to libconfig in Rust, using the peg crate to generate my parser.

For the last hour, I've been fighting with a weird bug where some values that were parsed from a sample config file didn't compare equal to the expected values.

Ultimately, I narrowed down the bug to this:

fn main() {
    let my_flt = "10.4e-5".parse::<f32>().unwrap();
    let other_flt = 10.4e-5;
    println!("{} == {} -> {}", my_flt, other_flt, my_flt == other_flt);
}

Surprisingly, this prints:

0.000104 == 0.000104 -> false

See it in the playground

Now, I know this has to be something related to the old infamous floating point precision issues. I know that even though two floats might look the same when printed, they can compare differently for various reasons, but I would have guessed that getting a float from parse::<f32>("X") would be equivalent to explicitly declaring and initializing a float to X. Clearly, I'm mistaken, but why?

After all, if I declare and initialize a float to X, internally, the compiler will have to do the same job as parse() when generating the final executable.

Why does the compiler parse the float X in the source file in a different way from the runtime parse::<f32>() function? Shouldn't this be consistent? I'm having such a hard time getting this over with!

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Filipe Gonçalves
  • 20,783
  • 6
  • 53
  • 70
  • 4
    Print at higher precision (e.g. `{:.20}`) and it becomes apparent they are different; transmute to `u32` to inspect the bytes, and yes, the first is 0x38da1a92 and the second 0x38da1a93. The mantissa is, as expected by the results, varying by one. – Chris Morgan Mar 27 '15 at 01:00
  • @MatthieuM. good point. moved and updated with a bit more info. – Shepmaster Mar 27 '15 at 14:08

1 Answers1

5

Note that the OP's specific example no longer fails (tested in Rust 1.31.0)


It's a known issue that the standard library and the rustc lexer parse floating point values differently.

The standard library ultimately calls down to from_str_radix, and you can see the implementation there. I'm not sure exactly where the compilers version of parsing floating-pint literals takes place, but this comment indicates it leverages LLVM:

The compiler uses LLVM to parse literals and we can’t quite depend on LLVM for our standard library.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • This is quite interesting. I wonder if they can't somehow mimic LLVM's behavior so as to make this consistent. It's probably not that easy, but I hope in the long run this gets fixed. Thanks for your answer! – Filipe Gonçalves Mar 27 '15 at 15:03
  • 1
    I think that the desire is that the two implementations act the same eventually. There's also a certain lack of performance in the current case, so that is probably on the table as well. Having one ULP of difference is probably not at the top of the pile, unfortunately. Maybe you can learn all about it and submit a patch! ^_^ – Shepmaster Mar 27 '15 at 15:44