由JSValue 计算引起的bug
公司在做订货方面的东西,在下单界面需要根据公式计算数据结果。我们公司H老大做了一套处理方法,利用API种JSContext自带的evaluateScript方法。之前没多少项目有小数的处理,但是最近有一家客户,单款商品单价包含两位小数,出现如下情况:
加法计算: (76.32) + (2.11) = 78.429999999
乘法计算: (76.82) * 10 = 768.200000001
后面的小数位数 可能有一定的出入,但是大致可以看出波动是在0.00000001,或者-0.00000001的出入。
方法使用错误?或者是精度缺失?
我查看了代码,发现我们返回数据是用的方法竟然是JSValue的toString方法,这个方法会把真正的数据类型再转为String,强转数据有可能会造成数据丢失。
- 我看API有toObject方法,就使用这个方法,但是返回的类型不是NSString了,我想按照类型再一个一个转,H领导说,为啥要那样做,不是有description吗?突然脑子反应过来,是呀,怎么把这个方法给遗忘了。
最后使用如下代码:
1 | JSContext *context = [[JSContext alloc] init]; |
但是依然有一部分数据计算结果是错误的。
👶很不开森,很郁闷,这是为什么呢?
我朋友问了他们的技术总监,对方说,有可能在计算的过程中精确缺失掉了。
- 突然想起,这个运算的处理是将字符串转为了数字,然后再计算的结果返回的,这么说倒是对的,然后就去查找JavaScript的计算处理,google js的精度丢失,发现还真有这么一个bug。
世界瞬间美好的不行不行滴,然后我就开始出各种馊主意
两种方法处理:
固定精度法;
计算数据的时候,直接调用toFixed(2),这样的形式保留两位小数。乘除位数法;
计算数据的时候,直接把数据转为整数,再除以整数倍数,如(数据 * 100)/100。
结果呢?!!
😒心情好的不行不行的。然而下午的时候java方面同事说不行。
第一种,固定精度,会造成精度后面的数据直接被丢弃,不进行四舍五入😓。
第二种,乘除位数吧,加法的时候 还是有多位,他说搞不定搞不定,不行呀不行呀,怎么办呢?
那怎么办,凉拌!!
四舍五入呀,我就去摸索四舍五入了。
断点调试,在lldb时输入
1 | po [[[JSContext alloc] init] evaluateScript:@"Math.round(76.8399999*100)/100"] |
Bing🐶,出现了我想要的结果,然后就让他们修改传入公式,然后就在演示前十三分钟搞定了。
总结
精度这种东西还是要注意的,尤其是在强制转换数据的时候,有时候不小心,或者稍微大意就会造成这种错误,虽然swift里对于强转对象会有warning显示,但是还是自己严谨处理代码好些,避免类似的bug重现。
附上JAvaScriptCore.h 的内容
1 | #ifndef JavaScriptCore_h |