对于以下代码(Java):
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
答案 0 :(得分:5)
int i = (int) (d * n); i == m;
对于m = 1,n = 49,这是假的。
i = (int) Math.round(d * n); i == m;
我的直觉告诉我它应该是真的,但可能很难严格证明。
答案 1 :(得分:4)
您提出的第二个问题涉及Java中ulp的大小。
如果ulp超过1/(n)
,则舍入乘法将无法恢复原始的划分int。通常,较大的ulps与较大的double值相关联。与双重相关的ulp在9E15左右开始超过1;如果你的恢复双打在那里,那么你可能会发现round()没有得到预期答案的问题。但是,在使用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。
答案 2 :(得分:1)
数学上应该是真的。但是,您可能会得到浮点舍入错误,这将导致错误。您几乎不应该使用==
比较浮点精度数字。
使用这样的阈值比较它们要好得多:
Math.abs( d*n - m ) < 0.000001;
请注意,这两个陈述应该是等价的
i = (int) (d * n);
i = (int) Math.round(d * n);
但是,例如,如果d=3/2
和n=2
,浮点错误可能会导致i=2.999999999999
在截断/舍入后为2。
答案 3 :(得分:1)
第一个绝对并非总是如此。第二个我会说是的,这是真的,但仅仅是因为我无法想到一个反例。
如果n非常大,可能是假的,我不确定。我知道至少99%的时间都是如此。