How to safely convert float to int in Rust

Viewed 517

How can I safely convert a floating point type (e.g. f64) to and integer type (e.g. u64)? In other words I want to convert the number but only if it actually can be represented by the target type.

I found a couple of questions that don't cover this and are not duplicates:

The solution is not to use as - that performs a saturating cast. Also u64::try_from(f64) is not implemented.

The closest seems to be f64::to_int_unchecked() but unfortunately it's unsafe. You can easily check the first two safety requirements (not NaN or infinity), but the third is a bit tedious: Be representable in the return type Int, after truncating off its fractional part.

The best I can come up with is to use as to convert it back to f64 and check equality, i.e.

fn convert(x: f64) -> Option<u64> {
    let y = x as u64;
    if y as f64 == x {
        Some(y)
    } else {
        None
    }
}

Is that the best option? Is it implemented anywhere?

1 Answers

Generally, I would defer what is best practice to the relevant lints used by Clippy. Clippy does a good job outlining the possible pitfalls of using x as y and offers possible solutions. These are all of the relevant lints I could find on the subject:

However if all you want is to find an answer of mapping f64 onto u64 without any precision loss, there are two conditions you will want to check:

  • x is an integer
  • x is within the bounds of the target type
pub fn strict_f64_to_u64(x: f64) -> Option<u64> {
    // Check if fractional component is 0 and that it can map to an integer in the f64
    // Using fract() is equivalent to using `as u64 as f64` and checking it matches
    if x.fract() == 0.0 && x >= u64::MIN as f64 && x <= u64::MAX as f64 {
        return Some(x.trunc())
    }

    None
}
Related