浮点数学被破坏了吗? [英] Is floating point math broken?

查看:28
本文介绍了浮点数学被破坏了吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下代码:

0.1 + 0.2 == 0.3  ->  false

0.1 + 0.2         ->  0.30000000000000004

为什么会出现这些错误?

Why do these inaccuracies happen?

推荐答案

二进制 浮点 数学就是这样.在大多数编程语言中,它基于 IEEE 754 标准.问题的关键是数字以这种格式表示为整数乘以 2 的幂;分母不是2的幂的有理数(例如0.1,即1/10)无法精确表示.

Binary floating point math is like this. In most programming languages, it is based on the IEEE 754 standard. The crux of the problem is that numbers are represented in this format as a whole number times a power of two; rational numbers (such as 0.1, which is 1/10) whose denominator is not a power of two cannot be exactly represented.

对于标准binary64格式的0.1,表示可以完全写成

For 0.1 in the standard binary64 format, the representation can be written exactly as

  • 0.1000000000000000055511151231257827021181583404541015625 十进制,或
  • 0x1.999999999999ap-4 C99 hexfloat 表示法.
  • 0.1000000000000000055511151231257827021181583404541015625 in decimal, or
  • 0x1.999999999999ap-4 in C99 hexfloat notation.

相反,有理数0.1,即1/10,可以完全写成

In contrast, the rational number 0.1, which is 1/10, can be written exactly as

  • 0.1 十进制,或
  • 0x1.99999999999999...p-4 类似于 C99 hexfloat 表示法,其中 ... 表示一个无休止的 9 序列.
  • 0.1 in decimal, or
  • 0x1.99999999999999...p-4 in an analogue of C99 hexfloat notation, where the ... represents an unending sequence of 9's.

程序中的常量 0.20.3 也将是它们真实值的近似值.碰巧最接近 0.2double 大于有理数 0.2 但最接近 doubledoublecode>0.3 小于有理数0.3.0.10.2 的总和最终大于有理数 0.3,因此与代码中的常量不一致.

The constants 0.2 and 0.3 in your program will also be approximations to their true values. It happens that the closest double to 0.2 is larger than the rational number 0.2 but that the closest double to 0.3 is smaller than the rational number 0.3. The sum of 0.1 and 0.2 winds up being larger than the rational number 0.3 and hence disagreeing with the constant in your code.

对浮点运算问题相当全面的处理是每个计算机科学家都应该了解的关于浮点运算的知识.有关更易于理解的解释,请参阅 floating-point-gui.de.

A fairly comprehensive treatment of floating-point arithmetic issues is What Every Computer Scientist Should Know About Floating-Point Arithmetic. For an easier-to-digest explanation, see floating-point-gui.de.

旁注:所有位置(以 N 为基数)数字系统都有这个问题

普通的旧十进制(基数为 10)数字也有同样的问题,这就是为什么像 1/3 这样的数字最终变成 0.333333333...

Plain old decimal (base 10) numbers have the same issues, which is why numbers like 1/3 end up as 0.333333333...

您刚刚偶然发现了一个数字 (3/10),它恰好很容易用十进制表示,但不适合二进制.它也是双向的(在某种程度上):1/16 在十进制 (0.0625) 中是一个丑陋的数字,但在二进制中,它看起来和十进制 (0.0001) 中的 10,000 一样简洁——如果我们在习惯在我们的日常生活中使用基数为 2 的数字系统,你甚至会看到那个数字并本能地理解你可以通过将某些东西减半,一次又一次地减半来达到目标​​.

You've just stumbled on a number (3/10) that happens to be easy to represent with the decimal system, but doesn't fit the binary system. It goes both ways (to some small degree) as well: 1/16 is an ugly number in decimal (0.0625), but in binary it looks as neat as a 10,000th does in decimal (0.0001)** - if we were in the habit of using a base-2 number system in our daily lives, you'd even look at that number and instinctively understand you could arrive there by halving something, halving it again, and again and again.

** 当然,浮点数在内存中的存储方式并不完全如此(它们使用一种科学记数法).然而,它确实说明了二进制浮点精度错误往往会突然出现这一点,因为现实世界"很复杂.我们通常感兴趣的数字通常是 10 的幂——但这仅仅是因为我们每天都使用十进制数字系统.这也是为什么我们会说 71% 而不是每 7 个中有 5 个"之类的话.(71% 是一个近似值,因为 5/7 不能用任何十进制数精确表示).

** Of course, that's not exactly how floating-point numbers are stored in memory (they use a form of scientific notation). However, it does illustrate the point that binary floating-point precision errors tend to crop up because the "real world" numbers we are usually interested in working with are so often powers of ten - but only because we use a decimal number system day-to-day. This is also why we'll say things like 71% instead of "5 out of every 7" (71% is an approximation, since 5/7 can't be represented exactly with any decimal number).

所以不:二进制浮点数没有被破坏,它们只是碰巧和其他所有以 N 为基数的数字系统一样不完美:)

So no: binary floating point numbers are not broken, they just happen to be as imperfect as every other base-N number system :)

旁注:在编程中使用浮点数

实际上,这个精度问题意味着在显示浮点数之前,您需要使用舍入函数将浮点数舍入到您感兴趣的小数位数.

In practice, this problem of precision means you need to use rounding functions to round your floating point numbers off to however many decimal places you're interested in before you display them.

您还需要用允许一定容忍度的比较替换相等性测试,这意味着:

You also need to replace equality tests with comparisons that allow some amount of tolerance, which means:

Do not do if (x == y) { ... }

改为if (abs(x - y) < myToleranceValue) { ... }.

其中 abs 是绝对值.需要为您的特定应用程序选择 myToleranceValue - 这与有多少摆动空间"有很大关系.您准备允许,以及您要比较的最大数字可能是多少(由于精度问题的损失).小心epsilon"您选择的语言中的样式常量.这些不能用作公差值.

where abs is the absolute value. myToleranceValue needs to be chosen for your particular application - and it will have a lot to do with how much "wiggle room" you are prepared to allow, and what the largest number you are going to be comparing may be (due to loss of precision issues). Beware of "epsilon" style constants in your language of choice. These are not to be used as tolerance values.

这篇关于浮点数学被破坏了吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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