Rounding f64 to the closest i64 in Rust

The type Rust f64 provides a function round() , which is rounded to the nearest integer, but returns f64 . Java Math.round(double) , on the other hand, returns long . I can call round() and then apply to i64 , but does this guarantee that I get the correct result? Here, “correct” means getting the closest i64 - Java round() returns “nearest long”.

+5
source share
3 answers

From the book , conversions from a floating-point number to an integer type are rounded to zero, so rounding off is almost correct at first: f.round() as i64 .

However, this behavior is currently undefined (but this is an error ) if f64 is out of range (huge amount) of i64 . Therefore, first you need to fix the value (or, perhaps, better, raise an error or approve). Perhaps the obvious answer doesn't work:

 f.max(std::i64::MIN as f64).min(std::i64::MAX as f64).round() as i64 

because the conversions from i64::MAX to f64 are not accurate, and applying the above to 1e100 ends up with a large negative value (in my test, as it was actually mentioned, undefined).

The best option is to return some error if the floating point value is outside the reasonable range expected by your application.

+5
source

You can use conv to do this:

 use conv::prelude::*; let x = 9_223_371_487_098_961_920i64 as f64; println!("{:?}", x.approx_as_by::<i64, RoundToNearest>()); // Ok(9223371487098962944) let x = 9_223_372_036_854_775_807i64 as f64; println!("{:?}", x.approx_as_by::<i64, RoundToNearest>()); // Err(FloatError::PosOverflow(..)) 
+5
source

Here is a simple implementation of the "back of the envelope":

 const INTEGRAL_LIMIT: f64 = 9007199254740992.0; #[derive(Debug, PartialEq, Eq)] enum Error { NaN, Overflow, Underflow, } fn try_from(f: f64) -> Result<i64, Error> { let f = f.round(); if f.is_nan() { return Err(Error::NaN); } if f < -INTEGRAL_LIMIT { return Err(Error::Underflow); } if f > INTEGRAL_LIMIT { return Err(Error::Overflow); } Ok(f as i64) } 

And it comes with a minimal test suite that passes:

 fn main() { assert_eq!(try_from(std::f64::NAN), Err(Error::NaN)); assert_eq!(try_from(std::f64::NEG_INFINITY), Err(Error::Underflow)); assert_eq!(try_from(-9007199254740994.0), Err(Error::Underflow)); assert_eq!(try_from( 9007199254740994.0), Err(Error::Overflow)); assert_eq!(try_from(std::f64::INFINITY), Err(Error::Overflow)); assert_eq!(try_from(-INTEGRAL_LIMIT), Ok(-9007199254740992)); assert_eq!(try_from( INTEGRAL_LIMIT), Ok( 9007199254740992)); } 

I really expected the implementation of TryFrom be available, but not found.

+1
source

Source: https://habr.com/ru/post/1261249/


All Articles