2

据我了解,有些数字不能用二进制精确表示,这就是为什么浮点运算有时会给我们带来意想不到的结果的原因;比如 4.35 * 100 = 434.99999999999994。类似于十进制的 1/3 的情况。

这是有道理的,但这引出了另一个问题。似乎在二进制中 4.35 和 435 都可以精确表示。那时它对我来说不再有意义。为什么 4.35 * 100 的计算结果为 434.99999999999994?435 和 4.35 在 double 类型动力学中有一个精确的表示:

double number1 = 4.35;
double number2 = 435;
double number3 = 100;
System.out.println(number1); // 4.35
System.out.println(number2); // 435.0
System.out.println(number3); // 100.0
// So far so good. Everything ok. 

System.out.println(number1 * number3); // 434.99999999999994 !!!
// But 4.35 * 100 evaluates to 434.99999999999994

为什么?

编辑:这个问题被标记为重复,它不是。正如您在接受的答案中看到的那样,我的困惑在于实际值和打印值之间的差异。

4

4 回答 4

12

似乎在二进制中 4.35 和 435 都可以精确表示。

我看到您了解浮点数的内部表示方式。至于您的疑问, no4.35​​没有确切的二进制表示。所以问题是,为什么第一个打印语句打印4.35.

发生这种情况是因为System.out.println()调用了该Double.toString(double)方法,该方法又使用FloatingDecimal#toJavaFormatString()方法,该方法在内部对传递的双参数执行一些舍入。您可以浏览我链接的源代码。

要查看 的实际值4.35,请尝试使用:

BigDecimal bd = new BigDecimal(number1);
System.out.println(bd);

这将打印:

4.3499999999999996447286321199499070644378662109375

在这种情况下,您创建一个将值作为参数BigDecimal传递的对象,而不是打印双精度值。表示任意精度有符号十进制数。因此,它为您提供了 的确切值。doubleBigDecimal4.35

于 2013-08-22T19:49:11.753 回答
8

你是对的,有时浮点运算会产生意想不到的结果。

您可以用浮点精确表示的断言4.35是不正确的,因为它不能表示为终止的二进制十进制。 100显然可以准确地表示,所以结果是434.99999999999994`4.35一定不能准确地表示。

要以浮点数精确表示,数字必须能够转换为分母仅为 2 的幂的分数(并且它不能太精确以至于超过浮点类型的最大精度) '正在使用)。在这种情况下,4.354 7/20,并且分母的因子是5,所以这个数字不能用二进制精确表示。

于 2013-08-22T19:43:39.553 回答
1

尽管从硬件的角度来看,每个浮点数都表示 M * 2^E 形式的某个精确值(其中ME是一定范围内的整数),但从软件的角度来看,将每个浮点数视为更有帮助表示“M * 2^E 被认为是最好的表示,并且希望接近那个的东西”。给定一个浮点值 (M * 2^E),人们应该认为它打算表示的实际数字很容易在 (N - 1/2) * 2^E 到 (N + 1/2) 之间的任何位置* 2^E 并且在实践中可能会延伸得更远一些。

举个简单的例子,对于 type float, 的值M被限制在 0-16777215 的范围内。因此 2000000.1f 的最佳表示是 16000001 * 2^-3 [即 16000001/8]。虽然 16000001/8 的精确十进制值是 2000000.125,但最后一位数字不是定义数字值所必需的,因为 16000001/8 是 2000000.120 和 2000000.129 的最佳表示(或者,就此而言,所有值介于 2000000.0625 和2000000.1875,不包括在内)。由于显示 M * 2^E 形式的数字的确切十进制值所需的位数通常远远超过有意义的位数,因此通常将显示的位数限制为大致唯一所需的位数定义值。

请注意,如果将浮点数视为表示范围,则会观察到从doubleto的float转换——即使它们必须明确指定——实际上是安全的,因为将double最能表示特定值的转换为float将产生最佳float表示该值或非常接近它的值。相反,从floatto的转换double,即使是隐式允许的,也是危险的,因为这种转换不太可能选择double最能代表应该代表的数字的float

于 2013-08-22T20:43:35.890 回答
1

用英语解释有点困难,因为我学过匈牙利语的计算机数字表示。总之,4.35、435和100都不完全是这些数字,而是尾数*2^k(k-特征从-k到+k,而t-是尾数在M = (t,-k, +k) ) 尽管 print 调用做了一些舍入。所以数线不是连续的,而是在一些著名的点附近,更密集)。

浮点数行

所以我认为这些数字并不完全符合您的期望,并且在操作之后(我想这是一个或两个简单的二进制操作),您会得到两个浮点数表示的误差距离的倍数。

于 2013-08-22T20:44:31.597 回答