这个文档没有特别好的解释,但是Double.toString(double)
本质上是对它产生的输出进行一些四舍五入。在整个Java SE中都使用了Double.toString
算法,包括例如PrintStream.println(double)
的System.out
。文档说这个:
多少位必须打印为米或一个小数部分?必须至少有一位数字来表示小数部分,并且除此之外还必须包含尽可能多的数字,但数量必须与唯一地区分参数值与类型为double
的相邻值所需的位数相同。也就是说,假设x是由该方法对有限非零参数产生的十进制表示法所表示的确切数学值d。然后d必须是最接近x的double
值;或者如果两个double
值同样接近X,然后d必须是其中之一,并d的有效数必须0
的至少显著位。
换句话说,它表示toString
的返回值不一定是参数的精确十进制表示形式。唯一的保证是(粗略地说)参数比任何其他double
值更接近返回值。
因此,当您执行类似System.out.println(1.10)
和1.10
的打印时,这并不意味着传入的值实际上等于基本10值1.10
。相反,本质上会发生以下情况:
- 首先,在编译过程中,字面
1.10
检查并四舍五入产生最接近double
值。 (它说在JLS here此规则是在Double.valueOf(String)
例如详细为double
。)
- 第二,当程序运行时,
Double.toString
产生一些十进制值,其在先前的步骤中产生的double
值接近的String
表示比任何其他double
值。
恰巧在第二步转换为String
经常产生一个String
,它与第一步中的文字相同。我会假设这是设计。无论如何,文字例如1.10
不会产生一个double
的值,它正好等于1.10
。
您可以使用BigDecimal(double)
构造发现一个double
的实际值(或float
,因为他们总能适合在double
):
当double
必须用作源的BigDecimal
,请注意,此构造函数提供了精确的转换;它不会给出与使用Double.toString(double)
方法将double
转换为String
然后使用BigDecimal(String)
构造函数相同的结果。要获得该结果,请使用static
valueOf(double)
方法。
// 0.899999999999999911182158029987476766109466552734375
System.out.println(new BigDecimal((double) (2.00 - 1.10)));
// 0.89999997615814208984375
System.out.println(new BigDecimal((float) (2.00 - 1.10)));
你可以看到,无论结果实际上是0.9
。在这种情况下,Float.toString
碰巧产生0.9
,Double.toString
不会产生或多或少的巧合。
作为一个附注,(double) (2.00 - 1.10)
是一个冗余的演员。 2.00
和1.10
已经是double
文字,所以评估表达式的结果已经是double
。此外,要减去float
,则需要投射两个操作数,如(float) 2.00 - (float) 1.10
或使用float
文字,如2.00f - 1.10f
。 (float) (2.00 - 1.10)
仅将结果转换为float
。
阅读[this](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) –
“也许默认情况下Java执行双倍计算”,因为'2.00'和'默认情况下,1.10'被认为是双打(除非你明确声明它们应该是浮动的,就像加上'f'后缀),所以'double + double = double'的结果解释了为什么要投射'(double)(2.00-1.10)不会改变任何东西。 – Pshemo
@biziclop,虽然涉嫌重复与此问题有关;这个版本的问题有所不同。所谓的重复不会解释这个问题的最基本的方面:为什么向下转换解决了这个问题? –