js 数字精确度

事情的起源: 项目中 填写的赔付金额是小数 传给后端需要 *100 9.87 *100 传给后端后是986.9999999999999 后端直接取整 就变成了9.86了

0.1 + 0.2 != 0.3

console.log(0.1 + 0.2) //0.30000000000000004
console.log(0.1 + 0.2 == 0.3) //false
1. 数字的存储

浮点数是用二进制的科学计算法来表示的,在计算机上是以二进制来进行存储的,单精度浮点数占用32位,双精度浮点数占用64位。

最高位是符号位(sign) , 0 表示正数, 1表示负数。接下来的11存储的是指数(exponent) , 最后是52位存储的是小数(mantissa)。浮点数的值可以用下面这个式子算出,类似于十进制的科学计数法。

注意以上的公式遵循科学计数法的规范,在十进制中 0<M<10,到二进制就是 0<M<2。也就是说整数部分只能是1,所以可以被舍去,只保留后面的小数部分。如 4.5 转成二进制就是 100.1,科学计数法表示是 1.001*2^2,舍去1后 M = 001。E是一个无符号整数,因为长度是11位,取值范围是 0~2047。但是科学计数法中的指数是可以为负数的,所以约定减去一个中间数 1023[0,1022] 表示为负,[1024,2047] 表示为正。如 4.5 的指数 E = 1025,尾数 M = 001

0.1 为例解释浮点误差的原因,0.1 转成二进制表示为 0.0001100110011001100(1100循环),1.100110011001100x2^-4,所以 E=-4+1023=1019;M 舍去首位的1,得到 100110011...在计算机中的存储为:

2. 0.1+0.2=0.30000000000000004?

转换成二进制计算:
0.00011001100110011001100110011001100110011001100110011010 +
0.0011001100110011001100110011001100110011001100110011010  =
0.0100110011001100110011001100110011001100110011001100111

// 十进制小数转二进制
小数部分*2 取整数

// 二进制小数转换成十进制
1*2^(-小数点后第几位)+1*2^(-小数点后第几位)....

9.87*100= 986.9999999999999

9.87 = 1001.110111101011100001010001111010111000010100011111 = 1.001110111101011100001010001111010111000010100011111 * 2^3

S = 0 E = 1026 M = 0011 1011 1101 0111 0000 1010 0011 1101 0111 0000 1010 0011 111

为什么x=0.1能得到0.1

二进制转换十进制的时候 小数的精度为2^(-52) ,即2.220446049e-16

所以数字转换成十进制的时候,JavaScript能表示的精度最多能精确到小数点后第16位,会把小数点后第17位进行凑整处理

0.1~0.9 21位有效数字处理结果

0.1.toPrecision(21) // 0.100000000000000005551
0.2.toPrecision(21) // 0.200000000000000011102
0.3.toPrecision(21) // 0.299999999999999988898
0.4.toPrecision(21) // 0.400000000000000022204
0.5.toPrecision(21) // 0.500000000000000000000
0.6.toPrecision(21) // 0.599999999999999977796
0.7.toPrecision(21) // 0.699999999999999955591
0.8.toPrecision(21) // 0.800000000000000044409
0.9.toPrecision(21) // 0.900000000000000022204

小数位16位处理后

0.1.toPrecision(16) // 0.1000000000000000
0.2.toPrecision(16) // 0.2000000000000000
0.3.toPrecision(16) // 0.3000000000000000
0.4.toPrecision(16) // 0.4000000000000000
0.5.toPrecision(16) // 0.5000000000000000
0.6.toPrecision(16) // 0.6000000000000000
0.7.toPrecision(16) // 0.7000000000000000
0.8.toPrecision(16) // 0.8000000000000000
0.9.toPrecision(16) // 0.9000000000000000
解决方案
  1. 自己手撸
  2. 现成: decimal.js number-precision long.js .....

相关推荐

  1. SpringBoot后端Long数据传到前端js精度损失问题

    2024-06-06 01:58:03       57 阅读
  2. vue.js怎么保证计算精度

    2024-06-06 01:58:03       59 阅读
  3. js关于数字的方法

    2024-06-06 01:58:03       29 阅读
  4. jsjs数组对象去重:

    2024-06-06 01:58:03       63 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-06 01:58:03       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-06 01:58:03       106 阅读
  3. 在Django里面运行非项目文件

    2024-06-06 01:58:03       87 阅读
  4. Python语言-面向对象

    2024-06-06 01:58:03       96 阅读

热门阅读

  1. 视觉SLAM

    2024-06-06 01:58:03       27 阅读
  2. 003 Spring注解

    2024-06-06 01:58:03       18 阅读
  3. nuxt3 api如何透传(不引第3方库)

    2024-06-06 01:58:03       28 阅读
  4. Lisp解析器技术文档

    2024-06-06 01:58:03       19 阅读
  5. Django 默认 CSRF 保护机制

    2024-06-06 01:58:03       34 阅读
  6. C语言编译与链接

    2024-06-06 01:58:03       31 阅读
  7. 设计模式(简要,应付软考)

    2024-06-06 01:58:03       25 阅读
  8. 概率图模型在自然语言处理中的应用

    2024-06-06 01:58:03       25 阅读
  9. AWS与SAP扩大战略合作:通过AI增强ERP解决方案

    2024-06-06 01:58:03       32 阅读
  10. 6月01日,每日信息差

    2024-06-06 01:58:03       28 阅读