最新消息:

每个Scratcher都应该知道的浮点运算

Scratch 少儿编程 2611浏览 0评论

友情提示:视频教程观看时请手动设置清晰度。

本文将会讨论Scratch的浮点运算及其衍生BUG。


 

 

01

 

起因

先来看一段代码

每个Scratcher都应该知道的浮点运算

这段代码的作用是获取用户输入的小数和期望保留的位数,并进行计算,最终输出计算结果。如输入圆周率的前七位——3.1415,保留小数点后3位,运算过程应该是这样的:3.1415*10的3次方,得到3141.5,进行四舍五入后得到3142,再除以10的三次方,最终得到的结果是应该是3.142,这个结果符合我们人工计算的结果,程序逻辑也没有问题。

 

那么来看一下程序运行结果

每个Scratcher都应该知道的浮点运算

最终变量result的值为3.142,符合预期,看起来,只要用户正常输入,这段程序应该是可以正常运行的。

那么换一个数字再来试试看,比如0.145,保留2位,人工计算答案是0.15没错吧?将这个数代入程序中看看?0.145*10的2次方,等于14.5,四舍五入得到15,再将15除以10的2次方,得到0.15,程序运行的结果应该也是符合人工计算的答案的。

 

来看一下实际运行结果吧

每个Scratcher都应该知道的浮点运算

结果竟然是0.14,而不是预期的0.15,程序竟然出错了,这是怎么回事呢?无论检查多少遍程序,似乎都是没有问题的,那么,问题到底出在哪里呢?

02

经过

在对程序进行逐步测试、拆分、简化后,最终定位到问题所在

每个Scratcher都应该知道的浮点运算

0.145*100=14.4999999……,没错,运算结果丢失了0.00……2.在对这个结果进行四舍五入之后得到的结果是14,而不是原本的15,最终0.00……2的精度丢失导致结果谬之千里。

再来看看其他的运算是否有这样情况

每个Scratcher都应该知道的浮点运算

可能你会想,不就是多了0.00……4吗?造成的影响也不会太大,那么来看看下面的试验

每个Scratcher都应该知道的浮点运算

震惊!在Scratch3.0中,0.1+0.2居然不等于0.3!

一点小小的精度丢失最后居然造成了本应该是“true“,但却是”false”这一天壤之别的结果,这足以引起每一个Scratcher的重视。

 

这难道又是Scratch3.0的一宗“罪”?

于是,在Scratch2.0中也做了同样的测试

每个Scratcher都应该知道的浮点运算

结果依旧令人震惊,这难道是Scratch的通病?

 

于是在谷歌浏览器的开发者控制台中进行测试

每个Scratcher都应该知道的浮点运算

JavaScript中也出现了同样的问题。


03

结果

其实,这是一个典型的浮点误差。这个现象不仅会在Scratch、JavaScript中出现,只要是采用IEEE(电气和电子工程师协会)754 浮点储存格式标准的语言,都会出现这样的问题,不同的语言会有不同的处理方式。至于问题出现的原因,应该归咎于两个环节:转换和储存。

转换

在计算机的计算中,只能存储和处理二进制数据,就是1和0。在计算时,首先要把十进制的数值转换为二进制,交给计算机处理,最后再把二进制的结果转换为十进制,显示到用户界面中。而在这个过程中,如果是十进制的小数转换成二进制,会是一个无限循环的小数。如十进制的0.9虽然只有一位小数,转成2进制是无限循环小数0.1110011001100110011…其实在十进制的0.1至0.9间的所有一位小数中,只有0.5能被完整地转换为二进制——0.1。

储存

计算机储存浮点数的精度有限,例float可以保留十进制最多7位(二进制23位)有效数字,double 可以保留十进制15~16位(二进制52位)有效数字。那有效数字以后的小数就被忽略了。

下面讲述使用IEEE754标准表示浮点数:

176.0625表示为单精度浮点数:

解:

1).先将176.0625转换为二进制数

 

小数点前:176 / 2 = 88  余数为 0

88 / 2=44 余数为 0

44 / 2 =22 余数为 0

22 / 2= 11 余数为 0

11 / 2 =5   余数为 1

5 / 2=2 余数为 1

2/ 2  =1 余数为 0

1/ 2=0        余数为 1    商为0,结束。

小数点前整数转换为二进制:10110000

 

小数点后:小数部分乘以2,取整数部分,直至乘积小数部分为0

0.0625 * 2 = 0.125   整数为0

0.125 * 2 = 0.25     整数为0

0.25* 2 = 0.50       整数为0

0.5* 2 = 1.0 整数为1,小数部分为0,结束

 

小数点后的小数位转换为二进制:0001

 

故176.0625转换为二进制为:10110000.0001

转自公众号:
Scratch格物堂

您必须 登录 才能发表评论!