Java中不可表示浮点数的精确表示
这似乎是一个非常愚蠢的问题,但我今天遇到了一些神秘而貌似美丽的事情。我试着用谷歌搜索,但什么都查不出来 我知道0.1不能用二进制表示。然而,当我运行以下代码时:Java中不可表示浮点数的精确表示,java,floating-point,Java,Floating Point,这似乎是一个非常愚蠢的问题,但我今天遇到了一些神秘而貌似美丽的事情。我试着用谷歌搜索,但什么都查不出来 我知道0.1不能用二进制表示。然而,当我运行以下代码时: double g = 1.0; System.out.println(g); g = g/10; System.out.println(g); g = g*3; System.out.println(g); 这将产生以下输出: 1.0 0.1 0.30000000000000004
double g = 1.0;
System.out.println(g);
g = g/10;
System.out.println(g);
g = g*3;
System.out.println(g);
这将产生以下输出:
1.0
0.1
0.30000000000000004
第一个输出和第三个输出是预期的,但是第二个输出发生了什么?为什么这是正确的?这应该是不可能的,但事实确实如此。数字升级规则
让我们逐行进行计算:
double g = 1.0;
g是float64数字,正好代表1.0
g = g / 10;
右操作数转换为double,因此精确为10.0
除法运算以无限精度(概念上)执行,然后四舍五入到最接近的浮点数64作为结果
确切答案显然是0.1。但最接近0.1的float64数字正好是7205759403792794/256
因此g=0.100000000000000555511512312578…(更多数字)。如果要打印全精度精确值,请查看新的BigDecimal(g)
再次将右操作数转换为3.0精确。我们将0.100000000000000551151231257(…)乘以3得到0.30000000000000000166533453693773(…)
现在g的值正好是5404319552844596/254
为什么这是正确的
正如您所指出的,许多十进制浮点数不能表示为二进制浮点数,反之亦然
当你写这样的陈述时:
double g = 0.1;
十进制值转换为最接近的二进制浮点值。然后你把它打印成这样
System.out.println(g);
格式化程序根据以下规则生成最接近的十进制浮点值:
小数部分必须打印多少位数?必须至少有一个数字来表示小数部分,除此之外,还必须有尽可能多的数字,但只能有尽可能多的数字来唯一区分参数值与double类型的相邻值
(参考资料:)
这意味着您通常会得到您开始使用的十进制数的精确十进制表示形式
简单来说,从十进制转换为二进制时的错误与从二进制转换为十进制时的错误相同。错误“取消”
现在,这种情况并不总是发生。通常,计算中的累积误差足够大,十进制(和二进制)结果中的误差将在输出中显示出来。您能否将您看到的输出包括在内,这样我们就不必编译和重新运行您的代码?他得到的输出可能是:
1.0
,0.1
,0.300000000004
在小数点右侧显示足够的数字,您将看到差异。请阅读。让我为子孙后代指出,如果您想避免浮点运算的这些问题,请使用类。@JimGarrison为什么您要让我这样做?它证明了许多关于IEEE浮点标准的事情,但与我的问题没有什么关系,而我的问题最终取决于toString()的意外行为。是的,但还有更多的故事,它证明Javadoc是错误的,而且(这并不影响您的答案)。
System.out.println(g);