System.out.println(2.00-1.10)
和
System.out.println((double)(2.00-1.10))
输出相同的结果0.8999999999999999
,但
System.out.println((float)(2.00-1.10))
输出0.9
。
也许默认情况下,Java执行double计算,那么为什么向下转换会纠正结果?
如果将1.1
转换为1.100000000000001
,则为什么
System.out.println((double)(1.10))
仅输出1.1
。
编辑:要了解为什么会发生这种情况,我们需要了解这两个答案。首先,规范表示在较低级别实际上是不同的。接下来,toString
的返回值如何更改/舍入/匹配最接近的参数的两倍。
答案 0 :(得分:8)
0.9不能表示为double或float。浮点计算的内部细节已经在SO上的各个帖子中得到了回答,例如:Is floating point math broken?。
在您的具体示例中,您可以使用以下代码查看最接近0.9的double和float:
System.out.println(new BigDecimal(0.9d));
System.out.println(new BigDecimal(0.9f));
输出0.9:
的规范double和float表示0.90000000000000002220446049250313080847263336181640625
0.89999997615814208984375
现在,当您计算2.0 - 1.1
时,结果为:
System.out.println(new BigDecimal(2.0-1.1));
0.899999999999999911182158029987476766109466552734375
你可以看到不是 0.9的规范表示,因此你会得到不同的结果。
但浮动精度不是很好,而且:
System.out.println(new BigDecimal((float) (2.0-1.1)));
0.89999997615814208984375
返回与0.9f的规范表示相同的数字。
答案 1 :(得分:4)
文档没有特别好地解释,但Double.toString(double)
基本上在它产生的输出中执行一些舍入。在整个Java SE中使用Double.toString
算法,包括例如PrintStream.println(double)
的{{1}}。文档说明了这一点:
m 或 a 的小数部分必须打印多少位数?必须至少有一个数字来表示小数部分,并且除此之外必须有多个,但只有多少,更多的数字才能唯一地将参数值与类型
System.out
的相邻值区分开来。也就是说,假设 x 是由此方法为有限非零参数 d 生成的十进制表示所表示的精确数学值。那么 d 必须是最接近 x 的double
值;或者如果两个double
值同样接近 x ,则 d 必须是其中之一,并且 d <的有效位的最低有效位/ em>必须是double
。
换句话说,它表示0
的返回值不一定是参数的精确十进制表示。唯一的保证是(粗略地说)参数比任何其他toString
值更接近返回值。
因此,当您执行类似double
和System.out.println(1.10)
的打印时,这并不意味着传入的值实际上等于基数10值1.10
。相反,基本上会发生以下情况:
1.10
并舍入以生成最接近的1.10
值。 (它在JLS here中说明了这一规则,例如double
double
详细说明。)Double.toString
会生成某个十进制值的String
表示形式,上一步中生成的double
值比任何其他double
更接近String
} value。恰好在第二步中转换为String
通常会产生1.10
,这与第一步中的文字相同。我认为这是设计的。无论如何,文字,例如double
未生成1.10
值,该值与double
完全相同。
您可以使用Double.valueOf(String)
构造函数发现float
(或double
的实际值,因为它们总是适合double
):
如果必须将
BigDecimal
用作double
的来源,请注意此构造函数提供了精确的转换;它与使用String
方法将Double.toString(double)
转换为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
文字。 {{1}}仅将结果转换为{{1}}。
答案 2 :(得分:1)
您将变量的实际值与值在屏幕上的显示方式混淆。在您的示例中,toString
方法用于在显示值之前将值转换为String。这使用默认值来使用多少个地方(double
和float
不同)。下面我明确设置要显示的位置数:
double d1 = 2 - 1.1;
float f1 = (float) d1;
System.out.println( String.format( "d1 = %.10f f1 = %.10f", d1, f1));
System.out.println( String.format( "d1 = %.20f f1 = %.20f", d1, f1));
System.out.println( String.format( "Using toString, d1 = %s f1 = %s", "" + d1, "" + f1));
给出输出:
d1 = 0.9000000000 f1 = 0.8999999762
d1 = 0.89999999999999990000 f1 = 0.89999997615814210000
Using toString, d1 = 0.8999999999999999 f1 = 0.9
这表明float
值不如double
值准确。