浮点数运算有误差的经典例子,浮点数有误差

  浮点数运算有误差的经典例子,浮点数有误差

  本文以Python为例,谈谈浮点运算为什么会产生错误。说说会导致错误的情况。以及如何解决?希望对你有帮助。

  【相关推荐:Python3视频教程】

  大家写代码都会遇到所谓的浮点错误。如果你还没踩到浮点错误的坑,只能说你很幸运。

  以下图中的Python为例。0.1 0.2不等于0.3,8.7/10不等于0.87,而是0.869999…,真的很奇怪。

  但这绝对不是地狱bug,也不是Python设计的问题,而是做运算时浮点数的必然结果,所以即使在JavaScript或其他语言中也是如此:

  00-1010在说为什么会有浮点误差之前,先说一下计算机是怎么用0和1来表示一个整数.的大家应该都知道二进制:比如101表示$2 2 2 0 $,也就是5,1010表示$2 3 2 1 $,也就是10。

  如果它是一个无符号的32位整数,这意味着它有32个位置可以放置0或1,所以最小值是0000.0000,即0,最大值为1111.1111,也就是2 {31} 2 {30}美元.2 1 2 0美元,也就是4294967295。

  从排列组合的角度来看,因为每一位都可以是0或1,整个变量的值有$2 {32} $的可能性,所以0到$2 {23}-1 $之间的任何值都可以用精确的表示,没有任何误差。

  00-1010虽然从0到$2 {23}-1 $之间有许多整数,但数字是有限,即$2 {32}。但是浮点数就不同了。我们可以这样想:1到10的范围内只有十个整数,但是有无穷多个浮点数,比如5.1,5.11,5.111等。不胜枚举。

  然而,在32位空间中只有两种可能性。为了把所有的浮点数都塞进这个32位的空间,很多CPU厂商发明了各种浮点数,但是如果每个CPU的格式不一样就麻烦了。所以采用了IEEE发布的IEEE 754作为通用的浮点数运算标准,现在所有的CPU都是按照这个标准设计的。

  

电脑是怎样储存一个整数的(Integer)

IEEE 754定义了很多东西,包括单精度(32 bit)、双精度(64 bit)、特殊值(infinity、NaN)等表达式。

  规格化

  以浮点数8.5为例。如果要改成IEEE 754格式,首先要做一些规范化:把8.5拆分成8 ^ 0.5,也就是$ 2 ^ 3(\ cf RAC { 1 } { 2 })1 $,然后写成二进制到1000.1,最后写成$1.0001 \乘以2 ^ 3 $和十进制。

  单精度浮点数

  在IEEE 754中,32位浮点数分为三部分,即符号、指数和分数,加起来是32位。

  符号:最左边的位代表符号,如果是正数,符号是0,否则,它是指数:中间的8位代表归一化的幂,它是阶码真值 +127格式。即3加127等于130的小数部分:最右边的23位是小数部分,在1.0001的情况下是1后面的0001。所以如果8.5用32位格式表示,应该是这样的:

  什么情况下会产生误差?

  前面8.5的例子可以表示为$ 2 ^ 3(\ cf RAC { 1 } { 2 })1 $,因为8和0.5正好是2的幂,所以根本不会有精度问题。

  但是如果是8

  .9 的话因为没办法换成 2 的次方数相加,所以最后会被迫表示成 $1.0001110011… \times 2^3$,而且还会产生大概 $0.0000003$ 的误差,如果对结果好奇的话可以到 IEEE-754 Floating Point Converter 网站上玩玩看。

  双精度浮点数

  前面所讲的单精度浮点数只用了 32 bit 来表示,为了让误差更小,IEEE 754 也定义了如何用 64 bit 来表示浮点数,跟 32 bit 比起来 fraction 部分扩大了两倍多,从 23 bit 变成 52 bit,所以精准度自然会提高许多。

  以刚才的 8.9 为例,用 64 bit 表示的话虽然可以变得更准,但因为 8.9 无法完全写成 2 的次方数相加,到了小数下 16 位仍然会出现误差,不过与单精度的误差 0.0000003 比起来已经小了很多

  类似的情况还有像 Python 中的 1.00.999...999 是相等的、123122.999...999 也是相等的,因为他们之间的差距已经小到无法放在 fraction 里面,所以从二进制格式看来它们每一个二进制位都是一样的。

  

解决方法

既然浮点数的误差是无法避免的,那就只好跟它共处了,下面是两个比较常见的处理方法:

  设定最大允许误差 ε (epsilon)

  在某些语言会提供所谓的 epsilon,用来让你判断是不是在浮点误差的允许范围内,以 Python 来说 epsilon 的值大约是 $2.2e^{-16}$

  所以你可以把 0.1 + 0.2 == 0.3 改写成 0.1 + 0.2 — 0.3 <= epsilon,这样就能避免浮点误差在运算过程中捣乱,正确的比较出 0.1 加 0.2 是不是等于 0.3 了。

  

当然如果系统没提供的话你也可以自己定义一个 epsilon,设定在 2 的 -15 次方左右
完全使用十进制进行计算

  之所以会有浮点误差,是因为把十进制转为二进制的过程中没办法把所有的小数部分都塞进了尾数中,既然转换可能会有误差,那干脆就不转了,直接用十进制来做运算。

  在 Python 里面有一个 module 叫做 decimal,在 JavaScript 中也有类似的包。它可以帮你用十进制来进行计算,就像你自己用纸笔计算 0.1 + 0.2 绝对不会出错、也不会有任何误差。

  虽然用十进制进行计算可以完全避免浮点数的误差,但因为 Decimal 的十进制计算是模拟出来的,在最底层的 CPU 电路中还是在用二进制进行运算,执行起来会比原生的浮点运算慢很多,所以不建议所有的浮点运算都用 Decimal 来进行。

  更多编程相关知识,请访问:编程入门!!以上就是浅谈浮点数运算为什么会产生误差的详细内容,更多请关注盛行IT软件开发工作室其它相关文章!

  

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: