可能为时已晚,但我无法理解此代码的行为:
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相同,我怀疑是双重特定的?但有人能解释一下吗?
答案 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例均应为非阳性
对于任意双d
,Math.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 * 1630
是double
最接近d / 1630
到1630
的实数乘法。它是实际结果的一半ULP。
d / 1630 * 1630
中的两个舍入操作都可以向上舍入,在这种情况下,d / 1630 * 1630
大于d
。预计不会超过d
ULP(d)
。
如果你想计算一个保证低于真实d / 1630的数字,你应该将舍入模式改为向下(不确定Java是否允许你这样做),或者从结果中减去一个ULP。 d / 1630
以默认的舍入到最接近的舍入模式计算。您可以使用函数nextAfter执行后者。