为什么Math类中的nextUp方法会跳过某些值?

时间:2016-05-11 13:57:52

标签: java floating-point precision

我只是在搞乱这种方法,看看它做了什么。我创建了一个值为3.14的变量,因为它在那个实例中出现了。

double n = 3.14;

System.out.println(Math.nextUp(n));

前面显示的是3.1400000000000006。

尝试使用3.1400000000000001,显示相同。

尝试333.33,显示333.33000000000004。

对于许多其他值,它会显示适当的值,例如73.6结果,73.60000000000001。

3.1400000000000000和3.1400000000000006之间的值会发生什么变化?为什么它会跳过某些值?我知道硬件相关的问题,但有时它的工作正常。即使已知无法进行精确操作,为什么这种方法包含在库中?它看起来很无用,因为它不能始终正常工作。

3 个答案:

答案 0 :(得分:2)

Java中一个有用的技巧是使用ActionBar和BigDecimal的new BigDecimal(double)的精确度来显示double的确切值:

toString

输出:

import java.math.BigDecimal;

public class Test {
  public static void main(String[] args) {
    System.out.println(new BigDecimal(3.14));
    System.out.println(new BigDecimal(3.1400000000000001));
    System.out.println(new BigDecimal(3.1400000000000006));
  }
}

存在有限数量的双精度数,因此只有实数的特定子集才是双精度的精确值。创建双字面值时,键入的十进制数字由最接近的值表示。当您输出一个double时,默认情况下,它会显示为输入时将舍入到它的最短小数部分。您需要执行我在程序中使用的BigDecimal技术,以查看确切的值。

在这种情况下,3.14和3.1400000000000001比任何其他双倍更接近3.140000000000000124344978758017532527446746826171875。上面的下一个完全可表示的数字是3.1400000000000005684341886080801486968994140625

答案 1 :(得分:1)

就像@jgreve提到的那样,由于使用了float和amp; java中的double primitives类型,这会导致所谓的舍入错误。另一方面,基本类型int是一个定点数,意味着它能够“适应”32位。双精度不是定点的,这意味着双重计算的结果必须经过舍入,以便适应其有限表示,这有时会导致(如您的情况所示)不一致的值。

有关详细信息,请参阅以下两个链接。

https://stackoverflow.com/a/322875/6012392

https://en.wikipedia.org/wiki/Double-precision_floating-point_format

解决方法可能是以下两个,它给出了第一个双重的“方向”。

double n = 1.4;
double x = 1.5;
System.out.println(Math.nextAfter(n, x)); 

double n = 1.4;
double next = n + Math.ulp(n);
System.out.println(next);

但是为了处理浮点值,建议使用BigDecimal

答案 2 :(得分:1)

浮点数以二进制形式存储:十进制表示仅供人类使用。

使用Rick Regan's decimal to floating point converter 3.14转换为:

11.001000111101011100001010001111010111000010100011111

和3.1400000000000006转换为

11.0010001111010111000010100011110101110000101001

这确实是下一个二进制数到53个有效位。