2011-08-10 168 views
5

对于下面的代码(JAVA):Java中如何将double转换为int?

double d = (double) m/n; //m and n are integers, n>0 
int i = (int) (d * n); 

i == m 

总是最后一个表达式是真的吗? 如果不是这总是正确的?:

i = (int) Math.round(d * n); 

i == m 
+1

不是一个愚蠢的问题;这个问题引发了一些关于浮点算法和整数“可恢复性”的微妙问题。 – Nayuki

+0

最近这个网站上有很多浮点问题 - 嗯... – 2011-08-10 17:56:23

回答

4

第二个问题,你问的关注如何ulp大是Java。

如果ULP超过1/(n),然后舍入乘法不会恢复原始分INT。通常,较大的ulps与较大的double值相关联。与double相关的ulp在9E15附近开始超过1;如果你的恢复双打在那里,那么你可能会发现问题与轮()没有得到预期的答案。但是,在使用int值时,分区的最大分子值将为Integer.MAX_VALUE

下面的程序测试所有的n正整数,看看哪一个会导致试图恢复被分割的INT当舍入误差最大的潜力:

public static void main(String[] args) 
    { 
    // start with large number 
    int m = Integer.MAX_VALUE; 
    double d = 0; 

    double largestError = 0; 
    int bigErrorCause = -1; 
    for (int n = 1; n < Integer.MAX_VALUE; n++) 
    { 
     d = (double) m/n; 
     double possibleError = Math.ulp(d) * n; 
     if (possibleError > largestError) 
     { 
     largestError = possibleError; 
     bigErrorCause = n; 
     } 
    } 
    System.out.println("int " + bigErrorCause + " causes at most " 
     + largestError + " error"); 
    } 

输出:

int 1073741823最多导致4。768371577590358E-7错误

舍入使用Math.round,然后转换为int应该恢复原始的int。

1

在数学上它应该是正确的。但是,您可能会得到浮点舍入错误,这会使其失败。您几乎不应该使用==来比较浮点精度数字。

你好得多使用这样的门槛比较它们:

Math.abs(d*n - m) < 0.000001; 

注意这两种说法应该是等价的

i = (int) (d * n); 
i = (int) Math.round(d * n); 

但是例如,如果d=3/2n=2,浮动可能会导致i=2.999999999999点误差,这在截去后/舍入为2

+3

你对截断的推理很好。但是你的例子很糟糕,因为浮点除以2总是精确的(除了下溢)。 ;-) – Nayuki

1

第一上是d无限期地不总是真实的。第二个我会说是的,这是真的,但只是因为我想不出一个反例。

如果n是非常大的,它可能是假的,我不知道真的。我知道至少有99%的时间会是真的。

5

int i = (int) (d * n); i == m;

这是对于m = 1为假,N = 49。

i = (int) Math.round(d * n); i == m;

我的直觉告诉我,它应该是真实的,但它可能是难以严格证明。

+0

即使在JavaScript中,您也可以验证我的声明。你可以在浏览器中输入:'javascript:1/49 * 49',它给出0.9999 ...。 – Nayuki

+2

+1由于'double'具有53位的精度,并且将它乘以小于2^31,结果应该小于1/2^21 = 2 * 1/2^22( 2的因素来自做两个操作)。所以四舍五入将会大幅度地确定整数。 – starblue

+0

我删除了我的帖子,因为我认为我错了第二种情况,尼斯答案(我已经给你我的+1) – MByD