Aardio - 关于数值计算精度的问题研究

★ 声明:因部分函数未公开算法,以下内容仅为本人测试推断,不保证与事实相符。

=======================================

先看一段代码:

var num1 = math.round( 0.575*100 )
console.log( num1 );

执行结果:

问题分析:

1、0.575*100=57.5

2、对 57.5进行四舍五入(默认舍弃小数,保留到个位数),结果应该是58,但实际结果为57。很显然这是错误的。

那么问题出在哪里呢?我们逐一分析,先看两张图:

    

同样是对57.5进行四舍五入,为什么结果不一样呢?

我觉得,实际上应该是这两个 57.5 的本质上,应该是不一样的。

我们深入测试一下:

这两个值还是一样的,没有发现猫腻。

继续深入,将这两个值放大看看:

呵呵,发现了,非常明显,两个值不一样。

知识点:因为计算机处理数值的机制问题,影响精度,57.5*100 计算的结果,其实不是 57.5 ,而是 57.49999999999999... 

但为什么aardio会显示为57.5呢?

继续看,查找本质,看看aardio为什么会让我们看到【两个一样的值】这么个假象呢?

经测试发现,aardio在显示数值时,从小数的第15位开始,会自动四舍五入。

所以,只要你的小数位数小于15位,是没有任何问题的,一旦达到15位,显示的数值和实际数值就会不一样。

为了继续确定 aardio 将第15位进行四舍五入的问题,我们进一步进行测试:

一波三折,又发现问题了,最后一位5,没向前进位。成了五舍六入了。

那我们测试一下,math.round() 是不是也这样:

额,这是咋了?

这……!!??

math.round() 出问题了,从第10位开始,就乱了。

math.round()只支持到小数点后第9位?!

数值放大测试一下

看来是有这种可能性。既然10位不准确,那么我们就验证一下9位是不是准确:

OK,继续放大数值看看:

基本上没有问题了。

看来,math.round() 自身就存在精度问题。所以,想依靠 math.round() 来处理到小数点后14位的想法,不好实现了。

结论

既然math计算出现了各种未知问题,最终如何保证我们处理的数值,跟显示的数值是一样的呢?

本来,我们只需要按以上规则,将数值进行四舍五入即可。

但既然 math.round() 有问题,而且console.log等函数也不仅仅是四舍五入这样简单,那么我只能折中一下,出两个不完美方案了。

1、因为console.log使用 tostring() 将数值变成字符串,而我们不知道tostring是如何工作的,所以,只能先利用tostring函数,再利用tonumber函数,进行一个【数值→字符串→数值】的转变过程。这样可以保证显示出来的数值,和真实的数值是一样的。

2、直接使用 math.round(,9) 进行处理,虽然处理后的数值小数位数不超过9位,但是这样既保证了与显示数值一致,也尽量保证了处理效率和准确度。

★ 建议:

牵扯到小数的计算式,尽量将计算结果做四舍五入处理,将可能出现的不精确结果,转化为一个可接受精度的固定数值。

不精确结果如:

0.1+0.1 = 0.20000000000000001  ;  

0.575×100 = 57.499999999999993  ;

0.14×100 = 14.0000000000000002  ;

一般这种不精确的结果,小数部分会很长,建议将其四舍五入至小数点后9位以内。

如:

math.round( 0.1+0.1 ,9 ) = 0.2  ;

math.round( 0.575×100 ,9 ) = 57.5  ;

math.round( 0.14×100 ,9 ) = 14  ;

最终效果如下:

    

猜你喜欢

转载自blog.csdn.net/sdlgq/article/details/112315335
今日推荐