为什么浮点数打印如此不同? [英] Why are floating point numbers printed so differently?

查看:141
本文介绍了为什么浮点数打印如此不同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(大多数)浮点数不是精确存储的(当使用IEEE-754格式时)。所以不应该这样做:

  0.3  -  0.2 === 0.1; //非常错误

...因为它会导致 false ,除非使用了一些特定的任意精度类型/类(BigDecimal在 Java / Ruby ,PHP中的 BCMath Math :: BigInt / )等等。然而,我想知道为什么当你试图打印结果这个表达式 0.3 - 0.2 ,脚本语言( Perl PHP )给出 0.1 ,但是虚拟机 ( Java JavaScript 和< erlang )给出了一些更类似于 0.09999999999999998 的东西,而不是?



为什么它在Ruby中也不一致? 版本1.8.6(键盘)给出 0.1 version 1.9.3(ideone)给出 0.0999 ... / p>

打印浮点数是一种转换操作:将以内部格式编码的值转换为十进制数字。但是,有关于转换细节的选择。



(A)如果您正在进行精确的数学计算并希望看到实际值由内部格式表示,那么转换必须是精确的:它必须产生一个与输入值完全相同的十进制数字。 (每个浮点数只能表示一个数字,IEEE 754标准中定义的浮点数不代表间隔。)有时,这可能需要产生大量的数字。

(B)如果你不需要确切的值,但是需要在内部格式和小数之间来回转换,那么你需要将它转换精确到十进制数字(和准确)足以区分它与任何其他结果。也就是说,您必须产生足够的数字,结果与通过转换以内部格式相邻的数字所得到的结果不同。这可能需要产生大量的数字,但不是太多以至于无法管理。



(C)如果您只想给读者可以了解数字,并且不需要为了让应用程序正常工作而生成确切的值,那么您只需要生成所需数量的数字就可以了。



哪一个应该进行转换?

不同的语言有不同的默认值,因为它们是为了不同的目的而开发的,或者是因为它不是(A)需要仔细的代码,并且一些语言或者它们的实现可以做到这一点不提供,也不保证提供这种行为。我相信,Java是我们所需要的。(b)
$ b (B)但是,正如我们在最近问题,它可能会有一些意想不到的行为。 ( 65.12 )打印为65.12,因为后者有足够的数字来区分附近的值,但是 65.12-2 打印为63.120000000000005,因为它和63.12之间还有一个浮点值,所以你需要额外的数字来区分它们。)

(c)是默认使用哪些语言。实际上,这是错误的,因为没有一个数字来打印多少位数可以适用于所有应用程序。事实上,我们已经看到,过去几十年来,它主要是通过隐瞒真实的价值观来引发对浮点的持续误解。然而,这很容易实现,因此对一些实现者来说很有吸引力。理想情况下,语言应该默认打印一个浮点数的正确值。如果要显示较少的数字,则应仅由应用程序实现者选择数字的数目,希望包括考虑适当的位数以产生期望的结果。


更糟糕的是,一些语言除了不显示实际值或足够的数字来区别外,甚至不保证所产生的数字在某种意义上是正确的(例如,通过将精确值舍入到显示的位数)。当在一个不能保证这种行为的实现中编程时,你并没有进行工程设计。


It's kind of a common knowledge that (most) floating point numbers are not stored precisely (when IEEE-754 format is used). So one shouldn't do this:

0.3 - 0.2 === 0.1; // very wrong

... as it will result in false, unless some specific arbitrary-precision type/class was used (BigDecimal in Java/Ruby, BCMath in PHP, Math::BigInt/Math::BigFloat in Perl, to name a few) instead.

Yet I wonder why when one tries to print the result of this expression, 0.3 - 0.2, scripting languages (Perl and PHP) give 0.1, but "virtual-machine" ones (Java, JavaScript and Erlang) give something more similar to 0.09999999999999998 instead?

And why is it also inconsistent in Ruby? version 1.8.6 (codepad) gives 0.1, version 1.9.3 (ideone) gives 0.0999...

解决方案

Floating-point numbers are printed differently because printing is done for different purposes, so different choices are made about how to do it.

Printing a floating-point number is a conversion operation: A value encoded in an internal format is converted to a decimal numeral. However, there are choices about the details of the conversion.

(A) If you are doing precise mathematics and want to see the actual value represented by the internal format, then the conversion must be exact: It must produce a decimal numeral that has exactly the same value as the input. (Each floating-point number represents exactly one number. A floating-point number, as defined in the IEEE 754 standard, does not represent an interval.) At times, this may require producing a very large number of digits.

(B) If you do not need the exact value but do need to convert back and forth between the internal format and decimal, then you need to convert it to a decimal numeral precisely (and accurately) enough to distinguish it from any other result. That is, you must produce enough digits that the result is different from what you would get by converting numbers that are adjacent in the internal format. This may require producing a large number of digits, but not so many as to be unmanageable.

(C) If you only want to give the reader a sense of the number, and do not need to produce the exact value in order for your application to function as desired, then you only need to produce as many digits as are needed for your particular application.

Which of these should a conversion do?

Different languages have different defaults because they were developed for different purposes, or because it was not expedient during development to do all the work necessary to produce exact results, or for various other reasons.

(A) requires careful code, and some languages or implementations of them do not provide, or do not guarantee to provide, this behavior.

(B) is required by Java, I believe. However, as we saw in a recent question, it can have some unexpected behavior. (65.12 is printed as "65.12" because the latter has enough digits to distinguish it from nearby values, but 65.12-2 is printed as "63.120000000000005" because there is another floating-point value between it and 63.12, so you need the extra digits to distinguish them.)

(C) is what some languages use by default. It is, in essence, wrong, since no single value for how many digits to print can be suitable for all applications. Indeed, we have seen over decades that it fosters continuing misconceptions about floating-point, largely by concealing the true values involved. It is, however, easy to implement, and hence is attractive to some implementors. Ideally, a language should by default print the correct value of a floating-point number. If fewer digits are to be displayed, the number of digits should be selected only by the application implementor, hopefully including consideration of the appropriate number of digits to produce the desire results.

Worse, some languages, in addition to not displaying the actual value or enough digits to distinguish it, do not even guarantee that the digits produced are correct in some sense (such as being the value you would get by rounding the exact value to the number of digits shown). When programming in an implementation that does not provide a guarantee about this behavior, you are not doing engineering.

这篇关于为什么浮点数打印如此不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆