四舍五入陌生

时间:2014-07-06 19:22:50

标签: java floating-point double floor

可能为时已晚,但我无法理解此代码的行为:

public class DT {
static void theTest(double d){
    double e = Math.floor(d/1630)*1630;
    System.out.println(e-d);
}

public static void main(String[] args) {
    theTest(2*1630);
    theTest(2*1631);
    theTest(2*1629);
    theTest(8.989779443802325E18);
}

}

在我的undertangind中,所有4个案例都应该是非正面的,即" e"总是< =" d", 但我得到以下输出:

0.0
-2.0
-1628.0
1024.0

么??。 因为这与FastMath相同,我怀疑是双重特定的?但有人能解释一下吗?

2 个答案:

答案 0 :(得分:5)

当你获得大量数据时,双打的间隔比整数更宽。在此范围内进行除法时,可以向上或向下舍入结果。所以在你的第四个测试用例中,除法d/1630的结果实际上是向上舍入到最近的可用双精度。由于这是一个整数,因此对floor的调用不会改变它。将其乘以1630,然后得出的结果大于d

修改

此效果在2 ^ 52时开始。一旦你超过2 ^ 52,就没有更多的非整数双打。在2 ^ 52和2 ^ 53之间,双精度只是整数。在2 ^ 53以上,双精度的间隔比整数更宽。

问题中除法的结果是5515202112762162.576...,它介于2 ^ 52和2 ^ 53之间。它舍入到最近的double,它与最接近的整数相同,即5515202112762163.现在,floor不会更改此数字,乘以1630会得到更大的结果比d

总而言之,我猜答案的第一句话有点误导 - 您不需要将双打间隔更多广泛而不是整数来发生此效果;你只需要将它们至少与整数一样

如果值d介于0和2 ^ 52 * 1630之间,则问题中的程序将永远不会输出正数。

答案 1 :(得分:2)

注意:我认为您正在寻找其他语言中的fmod和Java中的%操作。您希望计算的e - d可以计算为正确的符号,并始终低于1630 -(d % 1630.0)


  

所有4例均应为非阳性

对于任意双dMath.floor(d/1630)*1630可能小于d,但不是必需的。

按顺序:

d/1630是最接近真实d / 1630的double。它可以比实际d / 1630高出一半ULP,它可以任意接近一个整数。

d足够大时,d/1630始终是一个整数,因为任何足够大的double都是整数。换句话说,当d足够大时,Math.floor(d/1630)d/1630相同。这适用于您的上一个示例。

d / 1630 * 1630double最接近d / 16301630的实数乘法。它是实际结果的一半ULP。

d / 1630 * 1630中的两个舍入操作都可以向上舍入,在这种情况下,d / 1630 * 1630大于d。预计不会超过d ULP(d)

如果你想计算一个保证低于真实d / 1630的数字,你应该将舍入模式改为向下(不确定Java是否允许你这样做),或者从结果中减去一个ULP。 d / 1630以默认的舍入到最接近的舍入模式计算。您可以使用函数nextAfter执行后者。