关于javascript:都知道0102-030000000000000004那要怎么让它等于03

45次阅读

共计 2988 个字符,预计需要花费 8 分钟才能阅读完成。

前言

小学数学老师教过咱们,0.1 + 0.2 = 0.3,然而为什么在咱们在浏览器的控制台中输入却是 0.30000000000000004?

除了加法有这个奇怪的景象,带小数点的减法和乘除计算也会得出意料之外的后果

console.log(0.3 - 0.1) // 0.19999999999999998
console.log(0.1 * 0.2) // 0.020000000000000004
console.log(0.3 / 0.1) // 2.9999999999999996

起因

咱们都晓得计算机时是通过二进制来进行计算的,即 0 和 1

就拿 0.1 + 0.2 来说,0.1 示意为0.0001100110011001...,而 0.2 示意为0.0011001100110011...

而在二进制中 1 + 1 = 10,所以 0.1 + 0.2 = 0.0100110011001100...

转成 10 进制就近似示意为 0.30000000000000004

论断

简略来说就是,浮点数转成二进制时失落了精度,因而在二进制计算完再转回十进制时可能会和实践后果不同

对于浮点数的四则运算,许多编程语言都会有理论值和理论值不同的问题。例如 Java 中也会呈现相似的问题,然而 Java 中能够应用 java.math.BigDecimal 类来防止这种状况

可是 JS 是弱类型的语言,作者 Brendan Eich 自述 10 天内开发出 JS 语言,一开始设计的时候就没有对浮点数计算有个解决的好办法

那么在日常开发的前端我的项目中咱们能够怎么解决嘞?

解决方案

简略实现

应用 toFixed()< 不举荐 >

能够管制小数点后几位,如果为空的话会用 0 补充,返回一个字符串

> 0.123.toFixed(2) // '0.12'

毛病:

  • 在不同浏览器中得出的值可能不雷同,且局部数字得不到预计的后果,并不是执行严格的四舍五入
// 在 chrome 控制台中
> 1.014.toFixed(2) // '1.01'
> 1.215.toFixed(2) // '1.22'
> 1.105.toFixed(2) // '1.10'
> 1.115.toFixed(2) // '1.11'

乘以一个 10 的幂次方

把须要计算的数字乘以 10 的 n 次方,让数值都变为整数,计算完后再除以 10 的 n 次方,这样就不会呈现浮点数精度失落问题

> (0.1 * 10 + 0.2 *10) / 10  // 0.3

咱们能够将它封装成一个函数

mathFloat = function (float, digit) {const math = Math.pow(10, digit);
  return parseInt(float * math, 10) / math;
}
mathFloat(0.1 + 0.2, 3)  // 0.3

毛病:

  • JS 中的存储都是通过 8 字节的 double 浮点类型示意的,因而它并不能精确记录所有数字,它存在一个数值范畴

    Number.MAX_SAFE_INTEGER 为 9007199254740991,而 Number.MIN_SAFE_INTEGER 为 -9007199254740991,超出这个范畴的话 JS 是无奈示意的
    尽管范畴有限度,然而数值个别都够用

较为残缺的实现

加法

function mathPlus(arg1, arg2) {
  let r1, r2, m;
  try {r1 = arg1.toString().split(".")[1].length; // 获取小数点后字符长度
  } catch (error) {r1 = 0; // 为整数状态,r1 赋 0}
  try {r2 = arg2.toString().split(".")[1].length;
  } catch (error) {r2 = 0;}
  m = Math.pow(10, Math.max(r1, r2)); // 确保所有参数都为整数
  return (arg1 * m + arg2 * m) / m;
}
> mathPlus(0.1, 0.2); // 0.3
> mathPlus(1, 2); // 3

减法

function mathSubtract(arg1, arg2) {
  let r1, r2, m;
  try {r1 = arg1.toString().split(".")[1].length;
  } catch (error) {r1 = 0;}
  try {r2 = arg2.toString().split(".")[1].length;
  } catch (error) {r2 = 0;}
  m = Math.pow(10, Math.max(r1, r2));
  return ((arg1 * m - arg2 * m) / m);
}
> mathSubtract(0.3, 0.1); // 0.2
> mathSubtract(3, 1); // 2

乘法

function mathMultiply(arg1, arg2) {
  let m = 0;
  let s1 = arg1.toString();
  let s2 = arg2.toString();
  try {m += s1.split('.')[1].length; // 小数相乘,小数点后个数相加
  } catch (e) {}
  try {m += s2.split('.')[1].length;
  } catch (e) {}
  return ((Number(s1.replace('.', '')) * Number(s2.replace('.',''))) /
    Math.pow(10, m)
  );
}
> mathMultiply(0.1, 0.2); // 0.02
> mathMultiply(1, 2); // 2

除法

function mathDivide(arg1, arg2) {
  let m1 = 0;
  let m2 = 0;
  let n1 = 0;
  let n2 = 0;
  try {m1 = arg1.toString().split('.')[1].length;
  } catch (e) {}
  try {m2 = arg2.toString().split('.')[1].length;
  } catch (e) {}
  n1 = Number(arg1.toString().replace('.', ''));
  n2 = Number(arg2.toString().replace('.', ''));
   /**
   * 将除法转换成乘法
   * n1 / n2 必为整数
   * 乘以它们的小数点后个数差
   */
  return mathMultiply(n1 / n2, Math.pow(10, m2 - m1));
}
// > 0.2 / 0.03 => 6.666666666666667
> mathDivide(0.2, 0.03); // 6.666666666666665
> mathDivide(0.3, 0.1); // 3
> mathDivide(3, 1); // 3

引入第三方库

站在前人的肩膀上,能够后退的更快。上面这些成熟的库封装了很多实用的函数,尽管局部函数可能永远不会用到

Math.js

介绍:功能强大,内置大量函数,体积较大

Github 地址:https://github.com/josdejong/…

star: 12.2k+

decimal.js

介绍:反对三角函数等,并反对非整数幂

Github 地址:https://github.com/MikeMcl/de…

star: 4.8k+

big.js

介绍:体积 6k,提供了 CDN

Github 地址:https://github.com/MikeMcl/bi…

star: 3.9k+

number-precision

介绍:体积很小,只有 1k 左右

Github 地址:https://github.com/nefe/numbe…

star: 3.4k+

正文完
 0