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

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

问题描述

众所周知,(大多数)浮点数没有精确存储(使用 IEEE-754 格式时).所以不应该这样做:

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

... 因为它会导致 false,除非使用了某些特定的任意精度类型/类(Java/Ruby, BCMath 在 PHP 中,Math::BigInt/Math::BigFloat 在 Perl 中,仅举几例).

... 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.

然而我想知道为什么当人们试图打印这个表达式的结果时,0.3 - 0.2,脚本语言(PerlPHP) 给出 0.1,但是虚拟-机器"(JavaJavaScriptErlang) 提供更类似于 0.09999999999999998 的东西?

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?

为什么它在 Ruby 中也不一致?version 1.8.6 (codepad) 给出 0.1, 版本 1.9.3 (ideone) 给出 0.0999...

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) 如果你在做精确的数学运算,并希望看到内部格式所代表的实际值,那么转换必须是精确的:它必须产生一个完全相同的十进制数字值作为输入.(每个浮点数恰好代表一个数字.在 IEEE 754 标准中定义的浮点数不代表一个区间.)有时,这可能需要产生非常多的数字.

(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) 如果您不需要精确的值,但需要在内部格式和十进制之间来回转换,那么您需要将其精确地(准确地)转换为十进制数字) 足以将其与任何其他结果区分开来.也就是说,您必须生成足够多的数字,以使结果与通过转换内部格式中相邻的数字所得到的结果不同.这可能需要生成大量数字,但不能多到难以管理.

(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) 如果您只想让读者了解数字,而不需要生成确切的值以使您的应用程序按预期运行,那么您只需要生成您的特定应用所需的尽可能多的数字.

(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.

哪些是转换应该做的?

不同的语言有不同的默认值,因为它们是为不同的目的而开发的,或者因为在开发过程中无法完成所有必要的工作以产生准确的结果,或者出于各种其他原因.

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) 需要仔细的代码,并且某些语言或它们的实现不提供或不保证提供这种行为.

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

(B) 是 Java 所必需的,我相信.但是,正如我们在 最近的问题,它可能有一些意想不到的行为.(65.12 打印为65.12",因为后者有足够的数字来将它与附近的值区分开来,但 65.12-2 打印为63.1200000000000005",因为还有另一个浮动- 它和 63.12 之间的点值,所以你需要额外的数字来区分它们.)

(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) 是某些语言默认使用的.从本质上讲,这是错误的,因为没有一个单一的值可以适用于所有应用程序.事实上,几十年来我们已经看到,它主要是通过隐藏所涉及的真实值来助长对浮点数的持续误解.然而,它易于实现,因此对一些实现者很有吸引力.理想情况下,语言应该默认打印浮点数的正确值.如果要显示的位数较少,则位数应仅由应用程序实现者选择,希望包括考虑适当的位数以产生所需的结果.

(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天全站免登陆