比较“0.1”和 .1

Comparing "0.1" to .1

我正在努力了解 double 的局限性。 0.1 不能在有限系统中表示,因为它是一个重复的二进制值。但是,当我声明一个变量并给它 0.1 时,它仍然打印为 0.1 和 not .0999999999999999, 1.000000000000001 或类似的东西。但是,如果我添加一个 0.1 十次的变量,结果是 .9999999999999999,这正是我所期望的。

真正的问题是当我知道这是不可能的并且我有证据证明这不是真实值时,为什么它精确地打印为 0.1?

double d  = 0.1;
System.out.printf("%.16f\n",d);
System.out.printf("%.16f", d + d + d + d + d + d + d + d + d + d);

我想做的是将字符串文字与双精度转换进行比较,以查看字符串中的值是否可以用双精度精确表示。例如:

".5" == .5?预期答案:是。

".1" == .1?预期答案:否。

我正在尝试的比较是

number = new BigDecimal("0.1");
d = number.doubleValue();
if (number.compareTo(new BigDecimal(Double.toString(d))) == 0){
    // Doing something
}

任何帮助理解为什么这不能区分可以和不能用 double 表示的值将不胜感激。

要测试 String 是否代表 double 值,您可以这样做:

private static boolean isADouble(String s) {
    return new BigDecimal(s).equals(new BigDecimal(Double.parseDouble(s)));
}

public static void main(String[] args) {
    System.out.println(isADouble("0.1"));  // false
    System.out.println(isADouble("0.5"));  // true
}

这是有效的,因为 new BigDecimal(s) 产生 BigDecimal 正好等于 String 表示的值,而 new BigDecimal(Double.parseDouble(s)) 是 [=59= 的精确值]最接近该值 double

我会在最后解释为什么 number.compareTo(new BigDecimal(Double.toString(d))) == 0 有效。

当你

double d = 0.1;
System.out.println(d);

你得到答案 0.1 因为 d 最接近 double "real" 0.1。 (double d = 0.1;表示"make d the nearest double to 0.1"。当你写

System.out.println(d + d + d + d + d + d + d + d + d + d);

您看到 0.9999999999999999 而不是 1。您看不到 1 的原因是有 double 个值比答案更接近 1(实际上 1 本身就是 double 值)。没有理由为什么答案应该是最接近 double 的,因为 d 一开始并不是真正的 0.1 (它只是接近它),而且在任何情况下添加浮点数时的情况会引入错误。

最后,number.compareTo(new BigDecimal(Double.toString(d))) == 0 不起作用,因为即使 number.doubleValue() 不是 恰好 0.1Double.toString() 仍然会转换它到 String "0.1" 因为没有 double 值更接近 "real" 0.1.