python里7/3的值为什么会是2.3333333333333335,末位为什么会是5?
短回答:
因为浮点数的精度问题。
长回答:
7/3 结果是一个无限循环小数,而对于计算机,无限循环小数是无法保存的,要进行截断处理。
你可能会问四舍五入的话应该是2.3333333333333333才对啊,结尾怎么会是5呢?
其实,这里的截断处理是指在计算机浮点数表示法里进行截断,以64位浮点数为例,有1位表示正负,11位表示指数,52位表示尾数。
计算机所做的事情,是计算 7/3 的值,直到52位尾数全部占满位以后停止,也就是截断。
7/3 计算后为10.010101010101010101010101010101010101010101010101011,转化为十进制后就是2.3333333333333335,末尾出现5就不奇怪了。
PS:
在python3中,7/3的计算结果以浮点数来表示,为2.3333333333333335。
在python2中,7/3的计算结果以整数来表示,为2.
最后给题主加把劲儿!
回复细心的 @张博宇 提到的问题:一、10.01010....011,没数错的话似乎是53位数字(反正肯定是奇数位),而不是偶数位?
二、为什么截断的末尾是01011,而不是01010?
三、1.0/3,结果是0.33333...33,最后一位是3,是不是因为Python的二进制最后截断是0.01010101...0101?
首先指出一处小错误:一中不是53位,是51位。
开始答题:
我们来亲手算一下。7/3,在这里我们暂时把二进制小数点算到53位以更清晰地说明问题:
好戏要开始了,我们把上面这个能看花眼的二进制小数转化为科学记数法:
注意,由于左移了一位小数点,这时候小数点后有54位。
在64位浮点数中:1位符号为正,表示为‘0’;
11位指数(小数点位移量)为1,表示为‘10000000000’;
52位尾数为‘0010101010101010101010101010101010101010101010101011‘
为什么:尾部多出两位’10‘无法保留,所以0舍1入,在第52进一位,所以第52位的0变为1.
合起来,变为:
这也是计算机在内存中存储的方法。
那么读取的时候就要把它再拆过来:在尾数0010101010101010101010101010101010101010101010101011前面加上‘1.’:
指数为10000000000,即1,小数点右移1位:
注意此时小数点后有51位,这里回答了第一个问题为什么末尾是01011和第二个问题为什么是奇数。
符号为0,数字为正;
转化为十进制,即2.3333333333333335.
第三个问题:1.0/3:
二进制定点数为:0.1010101010101010101010101010101010101010101010101010符号为正,表示为‘0‘;
指数为-1(0.1010……变为01.010……),表示为’11111111110‘;
尾数表示为’0101010101010101010101010101010101010101010101010101‘
注意第52位为1,因为其本身就是1,第52位为0所以截断时未发生进位。
合起来:0111111111100101010101010101010101010101010101010101010101010101
换成十进制为0.3333333333333333.
PS:
感谢 @杨鑫逸 在我忙的时候帮我圆了场。
如果有1000本《哈姆雷特》,那么就有1000位莎士比亚。
本莎士比亚哦不,本菜鸟所用方法出自弗罗赞《计算机科学导论(第三版)》第三章’数据存储‘,若有错误请及时提醒。
感谢 @msoeg 的提醒:
64位浮点数转为十进制后有效数字为16位,所以第17位是不准确的。
在这里向知友们提出一个思考题:
0.1+0.2 == 17位有效数字;
而0.1+0.7 == 16位有效数字。
这是为什么?python输出位数不一致
十进制如何转二进制?
有知友发私信问我2.3333...如何转为10.010101...的,在这里解答一下疑惑。
首先弄清楚:十进制3在二进制是11,没错。但是十进制0.3在二进制就不是0.11了。
在这里为了更好地说明问题,我们取‘22.33’来做实验。
十进制小数转化为二进制小数有两部分:整数部分22和小数部分0.33。
先来处理整数部分‘22’:源为22,底为2,目标暂为空。
用底除源,商11余0. 目标为‘0’,新源为11.
用底除源,商5余1,目标为‘10’,新源为5.
用底除源,商2余1,目标为‘110’,新源为2.
用底除源,商1余0,目标为‘0110’,新源为1.
用底除源,商0余1,目标为‘10110’,结束。
二进制整数部分为‘10110’。整数部分转换过程
再来处理小数部分‘0.33’:源为0.33,底为2,目标暂为空,我们把目标位数定为7位。
用底乘源,得0.66,目标为‘0’,新源为0.66.
用底乘源,得1.32,目标为‘01’,新源为0.32.
用底乘源,得0.64,目标为‘010’,新源为0.64.
用底乘源,得1.28,目标为‘0101’,新源为0.28.
用底乘源,得0.56,目标为‘01010’,新源为0.56.
用底乘源,得1.12,目标为‘010101’,新源为0.12.
用底乘源,得0.24,目标为‘0101010’,结束。
二进制整数部分为0.0101010.小数部分转换过程
合起来:
得10110.0101010
PS:
此方法出自弗罗赞《计算机科学导论(第三版)》第二章‘数字系统‘,若有错误请及时提醒。
这种技术性回答有那么多人浏览,有点出乎意料。
所以,满足一下我滴好奇心:你们为什么点进来看这种‘枯燥’的技术问题?