c浮点数保留两位小数_0.1+0.20.3?(浮点数的二进制表示)

10db14d446e288724663beb062a92db9.gif

读到一本python的书,说0.1+0.2输出的结果不是0.3,这是为什么呢?

8f8e5aa8d4c79d84d0829c14f2572b3d.png

0.1+0.2≠0.3

这是计算机编程里一个常谈的问题:浮点数的存储。计算机存储都是用二进制存的,那么小数诸如0.1、0.2、0.3存储在内存中,使用二进制无法完全等值,只能无限接近,类似这样:

十进制二进制
0.10.0001 1001 1001 1001 …
0.20.0011 0011 0011 0011 …
0.30.0100 1100 1100 1100 …
0.40.0110 0110 0110 0110 …

就导致0.1+0.2 是两个二进制数相加,因为本身这两个数字在计算机底层存储的时候并不精确, 所以他们相加的结果和0.3对应的二进制值并不相等,只是非常接近。这就是为什么在有些编程语言中0.1+0.2!=0.3

1f0f009198b2f5c2fba02e6c9e342321.png

在有些编程语言比如我们这个程序中使用的C++中,编译器给我们做了一定程度的优化,当这个数字无限接近0.3的时候,它认为你想要的就是个0.3,那么它给你打印出来就是个0.3, 但是显示归显示,它在内存中存的值并没有变,还是一个0.299999…在底层仍然是一个很接近0.3的二进制数值的一个二进制数值。然后编译器再拿他进行赋值进行运算的时候,就出现了上面遇到的问题。

在这件事情上,python保留了一个"年轻人"应有的坦诚与直率。虽然这不符合数学逻辑,但是浮点数存储在二进制里,它只能做到这一步。

IEEE 754浮点数

在计算机系统的发展过程中,曾经提出过许多种实数的表达方法,比较典型的有相对于浮点数(Floating Point Number)的定点数(Fixed Point Number)。在定点数表达法中,其小数点固定地位于实数所有数字中间的某个位置。例如,货币的表达就可以采用这种表达方式,如 55.00 或者 00.55 可以用于表达具有 4 位精度,小数点后有两位的货币值。由于小数点位置固定,所以可以直接用 4 位数值来表达相应的数值。

dded7f0d4435f979ff442f94c6d9fba2.png

定点数表达法的缺点就在于其形式过于僵硬,固定的小数点位置决定了固定位数的整数部分和小数部分,不利于同时表达特别大的数或者特别小的数。因此,最终绝大多数现代的计算机系统都采纳了所谓的浮点数表达法。

浮点数表达法采用了科学计数法来表达实数,即用一个有效数字。一个基数(Base)、一个指数(Exponent)以及一个表示正负的符号来表达实数。比如,666.66 用十进制科学计数法可以表达为 6.6666×102(其中,6.6666 为有效数字,10 为基数,2 为指数)。浮点数利用指数达到了浮动小数点的效果,从而可以灵活地表达更大范围的实数。

56b1598827e7ac95698a3aae510698cd.png

浮点数表示法

直到 20 世纪 80 年代(即在没有制定 IEEE 754 标准之前),业界还没有一个统一的浮点数标准。很多计算机制造商根据自己的需要来设计自己的浮点数表示规则,以及浮点数的执行运算细节。另外,他们常常并不太关注运算的精确性,而把实现的速度和简易性看得比数字的精确性更重要,而这就给代码的可移植性造成了重大的障碍。

直到 1976 年,Intel 公司打算为其 8086 微处理器引进一种浮点数协处理器时,意识到作为芯片设计者的电子工程师和固体物理学家也许并不能通过数值分析来选择最合理的浮点数二进制格式。于是,他们邀请加州大学伯克利分校的 William Kahan 教授(当时最优秀的数值分析家)来为 8087 浮点处理器(FPU)设计浮点数格式。而这时,William Kahan 教授又找来两个专家协助他,于是就有了 KCS 组合(Kahn、Coonan和Stone),并共同完成了 Intel 公司的浮点数格式设计。

由于 Intel 公司的 KCS 浮点数格式完成得如此出色,以致 IEEE(Institute of Electrical and Electronics Engineers,电子电气工程师协会)决定采用一个非常接近 KCS 的方案作为 IEEE 的标准浮点格式。于是,IEEE 于 1985 年制订了二进制浮点运算标准 IEEE 754(IEEE Standard for Binary Floating-Point Arithmetic,ANSI/IEEE Std 754-1985),该标准限定指数的底为 2,并于同年被美国引用为 ANSI 标准。目前,几乎所有的计算机都支持 IEEE 754 标准,它大大地改善了科学应用程序的可移植性。

考虑到 IBM System/370 的影响,IEEE 于 1987 年推出了与底数无关的二进制浮点运算标准 IEEE 854,并于同年被美国引用为 ANSI 标准。1989 年,国际标准组织 IEC 批准 IEEE 754/854 为国际标准 IEC 559:1989。后来经修订后,标准号改为 IEC 60559。现在,几乎所有的浮点处理器完全或基本支持 IEC 60559。同时,C99 的浮点运算也支持 IEC 60559。

IEEE 浮点数标准是从逻辑上用三元组{S,E,M}来表示一个数 V 的,即 V=(-1)S×M×2E,如图所示。

8e25f9311bbbece6c57e28572a429563.png

其中:符号位 s(Sign)决定数是正数(s=0)还是负数(s=1),而对于数值 0 的符号位解释则作为特殊情况处理。

有效数字位 M(Significand)是二进制小数,它的取值范围为 1~2-ε,或者为 0~1-ε。它也被称为尾数位(Mantissa)、系数位(Coefficient),甚至还被称作“小数”。

指数位 E(Exponent)是 2 的幂(可能是负数),它的作用是对浮点数加权。

浮点数格式是一种数据结构,它规定了构成浮点数的各个字段、这些字段的布局及算术解释。

误差的产生—舍入误差

舍入误差是指运算得到的近似值和精确值之间的差异。大家知道,由于计算机的字长有限,因此在进行数值计算的过程中,对计算得到的中间结果数据要使用相关的舍入规则来取近似值,而这导致计算结果产生误差。

在浮点数的舍入问题上,IEEE 浮点格式定义了 4 种不同的舍入方式,如下表所示。其中,默认的舍入方法是向偶数舍入,而其他三种可用于计算上界和下界。

ba03b65b72b46fa5a49d7f22ac1ee870.png

需要特别说明的是,向偶数舍入(向最接近的值舍入)方式会试图找到一个最接近的匹配值。因此,它将 1.4 舍入成 1,将 1.6 舍入成 2,而将 1.5 和 2.5 都舍入成 2。

a1dddbb7ce63df9e3b77dbe079fefb22.png

浮点数的表示精度

一般提到浮点数的精度(有效位数)的时候,总是会出现 float的有效位为6~7位, double的有效位为15~16位 。

以float为例,有效位数只和规格化浮点数的尾数部分有关,而尾数部分的位数是23位,因此我们首先列出下表

f2294751bf6f1f3d960a49a552b22e8b.png

由上面的表格可以看出:

2^(−23) 和 2^(−22 )之间是存在间隔的,即0.0000001和0.0000002之间的小数我们是没有办法描述的,因此23位尾数最多只能描述到小数点后第7位;此外,我们通过四舍五入可以很容易发现

0.0000003=0.0000004=2^(−23)+2^(−22), 这表明第7位有效数字只是部分准确。而第6位及之前的都是可以准确描述的,因此我们说float的有效位为6~7位。

参考:

浮点数表示https://blog.csdn.net/shuzfan/article/details/53814424

IEEE 754浮点数标准详解http://c.biancheng.net/view/314.html

浮点数的二进制表示www.ruanyifeng.com

浮点数https://timroderick.com/floating-point-introduction/

b4c7647cbeb4a6675d04cbcb46007e69.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值