js计算精度问题, 这个函数是什么原理?

作者站长头像
站长
· 阅读数 8

我们知道, js里面的计算有时候是有误差的,是因为JavaScript使用二进制表示浮点数,因此不能精确表示所有十进制数

比如0.1 + 0.2不等于0.30.14 * 100不等于14

我一般是用这个函数处理的

Math.signFigures = function(num, rank = 6) {
  if(!num) return(0);
  const sign = num / Math.abs(num);
  const number = num * sign;
  const temp = rank - 1 - Math.floor(Math.log10(number));
  let ans;
  if (temp > 0) {
      ans = parseFloat(number.toFixed(temp));
  }
  else if (temp < 0) {
      ans = Math.round(number / Math.pow(10, temp)) * temp;
  }
  else {
      ans = Math.round(number);
  }
  return (ans * sign);
};

这个函数是从网上来的, 比如计算0.1 + 0.2, Math.sign(0.1 + 0.2)就很准确的等于0.3

我没明白这个函数的运行原理是啥, 看不懂

回复
1个回答
avatar
test
2024-07-07

数学函数

Math.log10(x) :以10为底的x的对数

Math.pow(x,y):x的y次方

Math.abs(x):x的绝对值

Math.floor(x):使x向下取整

console.log(Math.floor(5.95));//  output: 5
console.log(Math.floor(5.05));//  output: 5
console.log(Math.floor(5));//  output: 5
console.log(Math.floor(-5.05)); //  output: -6

x.toFixed:对x定点

Math.round(x):对x进行四舍五入

一些主要的代码

 const sign = num / Math.abs(num); 

上面是获取num的符号, 当num > 0, sign = 1; num < 0 , sign = -1; num = 0 情况 if(!num) return(0) 已经排除;

Math.log10(x) = y
Math.log10(1) = 0
Math.log10(10) = 1
Math.log10(100) = 2
Math.log10(1000) = 3
Math.log10(10000) = 4

上面x的位数比y的值多 1, 那么 y + 1 就等于 x的位数; 但是log10(x)大多数情况下存在小数, 比如log10(88) = 1.94448..; 对此可以使用Math.floor(x) 向下取整,Math.floor(Math.log10(88)) = 1;所以 1 + Math.floor(Math.log10(number) 就是计算num的整数位数;

Math.signFigures = function(num, rank = 6) {.......}

上面rank 默认设置为 6,也许是单精度浮点数在大多数平台能够保证6位有效数字; 基于此, 可以对原来的浮点数进行一些转换来解决丢失精度的问题。

  const temp = rank - 1 - Math.floor(Math.log10(number));
             = rank - (1 + Math.floor(Math.log10(number)));

1 + Math.floor(Math.log10(number) 是计算num的整数部分的位数, 令rank = 6, temp的含义是以6位有效数字的长度为分界点进行讨论:

  1. num的整数位数小于6时 temp > 0, 因为有效数位是6位,把num转化为一个定点数表示也没有问题,可以把temp设置为精确的小数部分(number.toFixed(temp));
  2. num的整数位数等于6时 temp = 0, 直接使用Math.round(number)进行四舍五入;
  3. num的整数位数大于6时 temp < 0, 此时要把num转化为一个整数; number / Math.pow(10, temp) 实际是把number扩大10的 |temp|次方倍

其他

可以这样:
if(Math.signFigures(x) == Math.signFigures(y)){
   //TODO:
}
但如果像下面这样,有时候会发现 x 与 value 相差很多
let value = Math.signFigures(x); // 令x = 10000001,或者令x的位数大于默认的6
回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容