2016-12-14 773 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可用,但沒有發現。