乘法时,0.9999999999999可以舍入到1.0吗?

2022-09-01 06:55:21

当将非常接近 1 的浮点数与 int > 0 相乘时,是否可以将其解释为 1。

也就是说,如果返回其最高可能结果(即比 1.0 低 1 步),则将Math.random()

(int)(Math.random() * 8)

是8岁还是7岁?

举一个实际的例子,这个经常使用的构造是否可以给出一个索引越界错误:

someArray[(int)(Math.random() * someArray.length)];

我对Java和ActionScript 3的答案特别感兴趣,但我认为它们都使用相同的浮点算术规则,任何平台的答案都会很有用。

更新:虽然我已经接受了一个答案,但我仍然希望确认这在ActionScript 3中也不会出错,因为一位同事报告说他曾经看到过它出错,这在一定程度上促使我问这个问题。


答案 1

如果将低于 1.0 的最大值乘以 (> 0),则结果将永远不会是 。someIntsomeInt

这可以对这样的整数进行详尽的测试:

Double greatestLessThanOne = Double.longBitsToDouble(4607182418800017407L);

// Assert that greatestLessThanOne is indeed the largest double less than 1.
//assert 1.0 == greatestLessThanOne + Math.ulp(greatestLessThanOne);

for (int i = 1; i >= 0; i++)
    if ((int) (greatestLessThanOne * i) == i)
        System.out.println("Exception found: " + i);

该代码段不生成任何输出。

(Math.ulp 返回给定的双精度值和下一个较大幅度的双精度值之间的距离。因此,该断言确保它确实是小于 1.0 的最大值。greatestLessThanOne

换句话说,您的生产线

Object element = elementArray[(int)(Math.random() * elementArray.length)];

永远不会产生 ArrayIndexOutOfBoundsException。


此外,根据马克·迪金森(Mark Dickinsons)在这里的评论,当乘以双倍时,这也成立。

使用IEEE 754浮点算术在舍入到最近模式下,您可以显示y对于任何和任何非微小的正数。(如果为非正态数或最小正态数,则可能会失败。x * y <x < 1.0yy


答案 2

只是圆它,可能是这样的:

BigDecimal bd = new BigDecimal(Double.toString(d));
bd = bd.setScale(decimalPlace,BigDecimal.ROUND_HALF_UP);