Skip to content

浮点数计算精度问题与mathjs #34

@jsonz1993

Description

@jsonz1993

浮点数精度问题

众所周知,js的浮点数计算一直被人诟病,最经典莫过于 0.1 + 0.2 !== 0.3
原因也很简单,计算机在做运算的时候都会把十进制转化为二进制来处理

0.1 -> 0.0001100110011001...(无限)
0.2 -> 0.0011001100110011...(无限)
0.1 + 0.2 -> 0.0100110011001100110011001100110011001100110011001100(IEEE 754)
再把二进制转为十进制就变成了: 0.30000000000000004

更详细的可以看 JavaScript 浮点数运算的精度问题

问题

我们已经熟知直接在浏览器端用js做计算经常出现各种奇怪问题,所以做系统的时候肯定都会提前考虑过这些,比如

  • 计算逻辑尽可能放在后端处理,避免多端处理重复与差异
  • 做的时候用一些小技巧,比如先乘后操作,避免出现浮点数
  • 用mathjs,最省心省力的方式

目前系统做投资相关业务,所以避免不了有大量数值类需要处理,所以在项目开始的时候,就引入mathjs作为计算的库。
但是前几天发现一个bug,是数值展示与实际的数值对不上,而且查了代码,这里确实有用到Mathjs计算的。

后面排查了才发现,mathjs 默认使用的是number 类型,我们需要手动去设置为 BigNumber 才能避免上面浮点数精度问题
image
至于原因,mathjs认为如果我们计算结果取四舍五入,是不会影响实际的展示效果的。

确实平时页面上数据的展示都是小数点后x位(2~6位),如果按照四舍五入去截取不会出现问题,系统的这个bug出现的原因就在于这一块的展示逻辑产品说直接截断小数点后两位,而不是采用四舍五入,所以就会导致计算结果和实际结果有差异。

-0.0006 * 10000 -> -5.99

解决方案

既然已经知道问题了,解决起来也方便,由于mathjs没办法做到全局配置
他的配置是要通过 create 接口去创建一个新的实例,也就是说,如果你一个地方用到,那你就要手动去create 一个 bigNumber类型的mathjs实例。
目前老项目有多个地方用到mathjs,一个一个地方去改是不可能的,所以直接配了一个 webpack.alias,把mathjs引到自己写的一个文件里,在文件中再去统一配置mathjs,相当于创建了一个mathjs单例。

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions