Rust: Floating-Point Numbers

Home · Blog

8 February 2023

The logical follow-up to my article Rust: Integers is one on floating-point numbers. Perhaps surprisingly, this one’s going to be simpler -- if you’ve read the one on integers, most of this article won’t be surprising.

Rust has two built-in types for floats, implementing 32-bit and 64-bit IEEE-754 standard numbers:

type   description           significand   exponent
f32    32-bit binary float       24 bits     8 bits
f64    64-bit binary float       53 bits    11 bits

Similar to integer literals, floating-point literals can have an f32 or f64 suffix. The default is f64. Underscores can be used as with integers.

let x = 123.45f32;         // 32-bit float
let y: f64 = 123.45;       // 64-bit float
let z = 123_456.789_012;   // also 64-bit float

The parse method also works for floats. One interesting details is that numbers too small or too large to be represented don’t result in an error:

let small =
    "0.0000000000000000000000000000000000000000000000000000000000000000001"
    .parse::<f32>();
let big = "1000000000000000000000000000000000000000".parse::<f32>();
println!("{:?}", small);   // prints Ok(0.0)
println!("{:?}", big);     // prints Ok(Inf)

This may seem inconsistent with how integer parsing works, but it’s in keeping with floating-point arithmetic, which will also silently produce zero or infinity values.

Casting with as is also similar. When casting from integers to floats, you can end up with a loss of precision -- if you know how floating-point numbers work, that makes sense, but the result can still look a bit surprising:

println!("{}", 333333333333333333u64 as f32);   // prints 333333340000000000

Going the other way you obviously lose the digits after the decimal point. Rust doesn’t try to do mathematically correct rounding here, it just “cuts off” the decimal digits. In more technical terms, it rounds towards zero:

println!("{}", 1.9f64 as i32);    // prints 1
println!("{}", -1.9f64 as i32);   // prints -1