骗人的 toFixed ( ) 方法

Q&A

Q:前端四舍五入并且保留两位小数的时候,应该怎么做?

A:Number.toFixed(2),就好了

平时在开发中,我们前端部分都是在用toFixed方法进行四舍五入并保留两位小数的,这种方式看上好像四舍五入是没有问题的,但一些特殊情况,还是有一些偏差。

计算机的运算一般都是不精确的,举个栗子:

这就涉及到了几个方面的问题:

存储(可能不精确)

我们把  0.2  变成二进制看一下

变成了0011 一直循环, 

但计算机的存储能力有限,当到了一定的长度后,后面的数字就不要了,当然,舍去的时候,也不是直接丢弃的舍去,而是看要保留位数的后一位是什么,再举个栗子:

0.00110011    保留六位时,后一位是1(入),所以得到 0.001101

0.00110011    保留5位时,后一位是0(舍),所以得到 0.00110

所以我们可以看到上面的 0.2 转换成二进制后,是以 01 结尾的,而不是0011这种

然后我们把   0.2 的十进制多保留几位小数,,这个时候就发现 0.2 的 存储 其实就是 不精确 的  

这样就会造成一个情况出现:​​​​​​​这两个值的再计算机里存储的内容是完全一样的 

那也就意味着这两个数字是完全相等的​​​​​​​

当然也不是所有的数字存储都是不精确的,​​​​​​​

运算(可能不精确)

因为运算就是用的存储的二进制进行运算的,但是结果显示的时候,是用十进制来显示的

不精确的 

又精确了?? 

计算机在运算的过程中,可能会存在一些抵消,有些不精确的存储可能会存的大一写,有些不精确的存储可能会存的小一点,这样在计算的时候一抵消,结果可能就变得精确了,举个栗子:

0.2的最后进行了入的操作,0.3的最后进行的舍的操作,一入一舍,刚好抵消 

 所以运算的过程中,是拿存储的二进制进行运算的

显示

这里有一个疑问:为什么这个地方的0.2又是正确的? 

 因为计算在显示的时候会做一个近似处理,0.200000000000001,就会显示 0.2


网上也有人说是 银行家算法

toFixed它是一个四舍六入五成双的诡异的方法(也叫银行家算法)

"四舍六入五成双"含义:对于位数很多的近似数,当有效位数确定后,其后面多余的数字应该舍去,只保留有效数字最末一位,这种修约(舍入)规则是“四舍六入五成双”

也即“4舍6入5凑偶”这里“四”是指≤4 时舍去,"六"是指≥6时进上,"五"指的是根据5后面的数字来定,当5后有数时,舍5入1;当5后无有效数字时,需要分两种情况来讲:①,5前为奇数,舍5入1;②,5前为偶数,舍5不进。(0是偶数)


然后在说回 toFixed ( ) 方法,这个方法是先运算再显示

2.45 保留一位小数 = 2.5 

2.55 保留一位小数 = 2.5

接下来就是一些解决 js 精度问题的方法:

1,自己写方法

numberCal = {

  // 加法运算

  add(a, b) {

    let c, d, e;

    try {

      c = a.toString().split(".")[1].length;

    catch (f) {

      c = 0;

    }

    try {

      d = b.toString().split(".")[1].length;

    catch (f) {

      d = 0;

    }

    return (

      (e = Math.pow(10, Math.max(c, d))), (this.mul(a, e) + this.mul(b, e)) / e

    );

  },

  // 减法运算

  sub(a, b) {

    let c, d, e;

    try {

      c = a.toString().split(".")[1].length;

    catch (f) {

      c = 0;

    }

    try {

      d = b.toString().split(".")[1].length;

    catch (f) {

      d = 0;

    }

    return (

      (e = Math.pow(10, Math.max(c, d))), (this.mul(a, e) - this.mul(b, e)) / e

    );

  },

  // 乘法运算

  mul(a, b) {

    let c = 0,

      d = a.toString(),

      e = b.toString();

    try {

      c += d.split(".")[1].length;

    catch (f) {

      //   console.log(`ERROR!!!!(╯°Д°)╯︵ ┻━┻`, f);

    }

    try {

      c += e.split(".")[1].length;

    catch (f) {

      //   console.log(`ERROR!!!!(╯°Д°)╯︵ ┻━┻`, f);

    }

    return (

      (Number(d.replace(".""")) * Number(e.replace("."""))) /

      Math.pow(10, c)

    );

  },

  // 除法运算

  div(a, b) {

    let c,

      d,

      e = 0,

      f = 0;

    try {

      e = a.toString().split(".")[1].length;

    catch (g) {

      console.log(`ERROR!!!!(╯°Д°)╯︵ ┻━┻`, g);

    }

    try {

      f = b.toString().split(".")[1].length;

    catch (g) {

      console.log(`ERROR!!!!(╯°Д°)╯︵ ┻━┻`, g);

    }

    return (

      (c = Number(a.toString().replace("."""))),

      (d = Number(b.toString().replace("."""))),

      this.mul(c / d, Math.pow(10, f - e))

    );

  },

};

使用

import { numberCal } from "./utils/index";

let a = 0.1;

let b = 0.2;

const num = numberCal.add(a, b);

console.log(num); // 0.3

2,Decimal.js

github地址:GitHub - MikeMcl/decimal.js: An arbitrary-precision Decimal type for JavaScript

中文API:decimal.js 中文API

npm install --save decimal.js  // 安装

import Decimal from "decimal.js"  // 具体文件中引入

使用

import Decimal from "decimal.js";

const a = 0.1;

const b = 0.2;

// a 与 b 可以是 任何类型,Decimal 内部会自己处理兼容

// 下面两种都可以 可以带 new 也不可以不带 new

const res1 = new Decimal(a).add(new Decimal(b));

const res2 = Decimal(a).add(Decimal(b));

console.log(res1.toNumber(), res2.toNumber()); // 0.3  0.3

3,Math.js

链接:math.js | an extensive math library for JavaScript and Node.js

npm install --save math.js  // 安装

import * as math from "mathjs";

// 保留一位小数

console.log(math.round(2.45, 1)); // 2.5

// 保留一位小数

console.log(math.round(2.55, 1)); // 2.6

// 0.1 + 0.2

console.log(math.add(math.bignumber(0.1), math.bignumber(0.2)).toString()); // 0.3

4,big.js

链接:https://mikemcl.github.io/big.js/#

npm install --save big.js  // 安装

import Big from "big.js";

const number1 = Big(0.1);

const number2 = Big(0.2);

const number3 = number1.plus(number2).toString();

console.log(number3, "number3"); // 0.3

5,*100 / 100

// 解决精度问题

console.log((0.1 * 10 + 0.2 * 10) / 10); // 0.3

建议

当涉及金额计算时,慎用 toFixed ( ) 方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值