在Rust中将f64舍入到最近的i64

时间:2016-12-14 09:06:38

标签: floating-point rust precision

Rust的f64类型提供函数round(),它舍入到最接近的整数,但返回f64。另一方面,Java的Math.round(double)返回long。我可以拨打round()然后转发给i64,但是这会保证我得到正确的结果吗?在这里,“正确”表示获得最接近的i64 - Java的round()返回“最近的长”。

3 个答案:

答案 0 :(得分:8)

the book开始,从浮点到整数类型的转换向零舍入,因此首先舍入几乎正确:f.round() as i64

但是,如果f64超出范围(巨大幅度)i64,它目前也是未定义的行为(但这是a bug)。因此,您应该首先钳制该值(或者可能更好,引发错误或断言)。可能显而易见的答案并不奏效:

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

因为i64::MAXf64的转换并不准确,将上述应用到1e100会产生很大的负值(在我的测试中;如上所述& #39; s实际上是未定义的。)

如果浮点值超出了应用程序期望的合理范围,那么最好的选择似乎是返回一些错误。

答案 1 :(得分:7)

您可以使用conv包装箱:

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(..))

答案 2 :(得分:1)

这是一个简单的&#34;信封背面&#34;实现:

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)
}

它带有一个最小的测试套件,通过:

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));
}

我实际上期待TryFrom实现可用,但没有找到。