出现情景:

  1. 0.1+0.2 // 0.30000000000000004
  2. 0.1+0.7 // 0.7999999999999999
  3. 0.1+0.3 // 0.4

Number类型相关:

Js中的数字类型只有Number一种,采用IEEE-754标准中的“双精度浮点数”表示一个数字,并没有再去区分整数类型和浮点数类型。

IEEE-754标准表示法:

一种二进制表示法,能够精确地表示分数,比如1/2,1/8,1/1024。遗憾的是,我们常用的分数都是十进制分数1/10,1/100等,二进制浮点数表示法并不能精确地表示0.1这样的简单十进制数字,因为用二进制表示是无限循环,如下:

十进制整数转二进制
  1. 1=>1 2=>10 3=>101 4=>100 5=>101 6=>110
  2. 0.1 => 0.0001 1001 1001 1001…(无限循环)
  3. 0.2 => 0.0011 0011 0011 0011…(无限循环)

存储结构:

在IEEE-754中,双精度浮点数(double precision)占用64bit来存储,如下:
Javascript浮点数计算精度问题处理 - 图1

数值范围:

从存储结构中可以看出, 指数部分的长度是11个二进制,即指数部分能表示的最大值是 2047(2^11-1);
取中间值进行偏移,用来表示负指数,也就是说指数的范围是 [-1023,1024]
因此,这种存储结构能够表示的数值范围为 2^1024 到 2^-1023 ,超出这个范围的数无法表示 。2^1024 和 2^-1023 转换为科学计数法如下所示:
最大值 1.7976931348623157 × 10^308
最小值 5 × 10^-324

这两个边界值通过固定变量来获取:

  1. Number.MAX_VALUE; // 1.7976931348623157e+308
  2. Number.MIN_VALUE; // 5e-324
  3. // 超出这两个边界的话
  4. Number.MAX_VALUE*10; // Infinity
  5. 1.7976931348623157E+309; // Infinity
  6. -1.7976931348623157E+309; // -Infinity

数值精度:

在 64 位的二进制中,符号位决定了一个数的正负,指数部分决定了数值的大小,小数部分决定了数值的精度
JavaScript 能表示并进行精确算术运算的整数范围为:[-2^53-1,2^53-1],即从最小值 -9007199254740991 到最大值 9007199254740991 之间的范围 。

  1. Math.pow(2, 53)-1; // 9007199254740991
  2. -Math.pow(2, 53)-1; // -9007199254740991
  3. Number.MAX_SAFE_INTEGER; // 9007199254740991
  4. Number.MIN_SAFE_INTEGER ; // -9007199254740991

超过这个范围的整数,JS依旧可以运算,但不保证运算结果的精度。

  1. Math.pow(2, 53) ; // 9007199254740992
  2. Math.pow(2, 53) + 1; // 9007199254740992
  3. 9007199254740993; //9007199254740992
  4. 90071992547409921; //90071992547409920
  5. 0.923456789012345678;//0.9234567890123456


数字精度丢失的经典问题处理:

浮点数相加

  1. 0.1 + 0.2 != 0.3 // true

toFixed的处理不会四舍五入【有些Chrome可以自动四舍五入,但是也有不会四舍五入存在】

  1. (2.345).toFixed(2) // "2.34"
  2. // mac下的版本 75.0.3770.100(正式版本) (64 位)正常四舍五入

解决方法:

浮点数的四则运算处理:

  1. /**
  2. * 判断obj是否为一个整数
  3. */
  4. function isInt(param) {
  5. return Math.floor(param) === param;
  6. }
  7. /**
  8. * 将一个浮点数转成整数,如:3.14 => 314,倍数是100
  9. * @param floatNum {number} 小数
  10. * @return {times: 100, num: 314} {object} 对象
  11. */
  12. function toInt(floatNum) {
  13. let res = {
  14. times: 1,
  15. num: 0
  16. }
  17. // 是否负数
  18. let isNegative = floatNum < 0;
  19. // 整数就直接返回
  20. if (isInt(floatNum)) {
  21. res.num = floatNum;
  22. return res;
  23. }
  24. let len = String(floatNum).split(".")[1].length;
  25. let times = Math.pow(10, len);
  26. // 为了避免乘以times之后,得到一个无限小数比原值小或者大,所以加上0.5利用parseInt取整
  27. let intNum = parseInt(Math.abs(floatNum) * times + 0.5, 10);
  28. if (isNegative) {
  29. intNum = -intNum;
  30. }
  31. res.times = times;
  32. res.num = intNum;
  33. return res;
  34. }
  35. /**
  36. * 实现加减乘除,去报不丢失精度
  37. * @param a {number} 数字a
  38. * @param b {number} 数字b
  39. * @param op {string} 运算类型 加减乘除 add/subtract/multiply/divide
  40. */
  41. function opration(a, b, op) {
  42. let aObj = toInt(a);
  43. let bObj = toInt(b);
  44. // 整数
  45. let aNum = aObj.num;
  46. let bNum = bObj.num;
  47. // 倍数
  48. let aTimes = aObj.times;
  49. let bTimes = bObj.times;
  50. // 最大倍数,初始化
  51. let maxTimes = aTimes;
  52. // 同步两个参数大小
  53. if (aTimes > bTimes) {
  54. bNum = bNum * (aTimes / bTimes);
  55. maxTimes = aTimes;
  56. } else if (aTimes < bTimes) {
  57. aNum = aNum * (bTimes / aTimes);
  58. maxTimes = bTimes;
  59. }
  60. // 四则运算
  61. let result = 0;
  62. switch (op) {
  63. case "add":
  64. result = (aNum + bNum) / maxTimes;
  65. break;
  66. case "subtract":
  67. result = (aNum - bNum) / maxTimes;
  68. break;
  69. case "multiply":
  70. result = (aNum * bNum) / Math.pow(maxTimes, 2);
  71. break;
  72. case "divide":
  73. result = aNum / bNum;
  74. break;
  75. }
  76. return result;
  77. }
  78. // 加法
  79. export let add = (a, b) => {
  80. return opration(a, b, "add");
  81. }
  82. // 减法
  83. export let subtract = (a, b) => {
  84. return opration(a, b, "subtract")
  85. }
  86. // 乘法
  87. export let multiply = (a, b) => {
  88. return opration(a, b, "multiply");
  89. }
  90. // 除法
  91. export let divide = (a, b) => {
  92. return opration(a, b, "divide")
  93. }

微信公众号:**游戏机室
qrcode_for_gh_0e7f620a68c0_258.jpg**