Rust's f64
type provides the function round()
, which rounds to the nearest integer, but it returns a f64
. Java's Math.round(double)
, on the other hand, returns a long
. I can call round()
and then cast to i64
, but will this guarantee that I get the correct result? Here, "correct" means getting the closest i64
— Java's round()
returns the "closest long".

- 388,571
- 95
- 1,107
- 1,366

- 3,583
- 16
- 32
-
4What do you want to happen if the `f64` is integral but out of range of `i64`? – Chris Emerson Dec 14 '16 at 09:33
-
@ChrisEmerson in this case, Java appears to round `1e100` to `9223372036854775807`. – Shepmaster Dec 14 '16 at 13:40
3 Answers
From the book, conversions from floating point to integer types round towards zero, so rounding first is nearly correct: f.round() as i64
.
However, it's also currently undefined behaviour (but this is a bug) if the f64
is out of range (huge magnitude) of i64
. Therefore you should clamp the value first (or possibly better, raise an error or assert). The possibly obvious answer doesn't work:
f.max(std::i64::MIN as f64).min(std::i64::MAX as f64).round() as i64
because the conversions of i64::MAX
to f64
aren't exact, and applying the above to 1e100
ends up with a large negative value (in my test; as mentioned it's actually undefined).
The best option seems to be to return an error of some if the floating point value is out of the reasonable range your application expects.

- 13,041
- 3
- 44
- 66
-
It seems you would need a way to obtain the lower and upper limits of the contiguous range of integral values that fits into a `f64` to be able to either clamp or raise an exception. Otherwise, because of undefined behavior, you cannot actually check the conversion. Maybe the language should expose [those](http://stackoverflow.com/questions/3793838/which-is-the-first-integer-that-an-ieee-754-float-is-incapable-of-representing-e). – Matthieu M. Dec 14 '16 at 09:57
-
I agree, in the general case. But there's probably a limit below those which makes sense for any (ok, most) given application. – Chris Emerson Dec 14 '16 at 10:05
-
Given that we are talking about 253, yep, there's probably a good limit! – Matthieu M. Dec 14 '16 at 10:08
-
This bug was finally fixed in Rust 1.45, released July 2020: https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#whats-in-1450-stable – Jason Orendorff Aug 05 '22 at 16:38
You can use the conv
crate for 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(..))

- 55,277
- 5
- 189
- 162
Here is a simple "back of the envelope" implementation:
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 which 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 was actually expecting a TryFrom
implementation to be available, but found none.

- 287,565
- 48
- 449
- 722