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