2016-12-14 772 views
5

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

+4

如果'f64'是整数,但超出'i64'的范围,你会发生什么? –

+0

@ChrisEmerson在这种情况下,Java似乎将'1e100'到'9223372036854775807'。 – Shepmaster

回答

5

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具有较大的负值(在我的测试结束了,因为提到它实际上未定义)。

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

+0

看来你需要一种方法来获得适合'f64'的整数值的连续范围的下限和上限,以便能够钳位或引发异常。否则,由于未定义的行为,您实际上无法检查转换。也许语言应该公开[那些](http://stackoverflow.com/questions/3793838/which-is-the-first-integer-that-an-ieee-754-float-is-incapable-of-representing-e )。 –

+0

我同意,在一般情况下。但是,对于任何(确定的,大部分)给定的应用程序,这可能有一个限制。 –

+0

鉴于我们正在谈论2 ,是的,这可能是一个很好的限制! –

5

可以使用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(..)) 
1

下面是一个简单的“回信封”执行:

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 implementa可用,但没有发现。