js数字四舍五入,toFixed() 方法遵循的规则你知道吗?
() 方法将数值转换为一个字符串,该字符串表示该数值与指定小数位数的最近近似值。当需要四舍五入时,它遵循IEEE 754标准中的四舍五入规则,这与一般的数学四舍五入规则有所不同。
语法:
.()
:指定小数位数。
() 会将数字四舍五入到指定的小数位数,并返回一个字符串。如果小数位数不足,会用 0 填充。
() 遵循银行家舍入法,但有些时候和好像和银行家舍入法的结果不同,其实这是我们误会 () 了,主要是因为 浮点数在用二进制中存储时有精度误差导致 () 用银行家四舍五入时不准。
银行家舍入法
我国金融系统的大部分算法就是用四舍五入,国际上欧盟委员会对换汇时的舍入规定也是我们常见的四舍五入。
真正广泛采用银行家舍入法的,是需要更小误差的科学和计算机系统,因为多次舍入时银行家舍入法会比传统舍入法误差更小,因此银行家舍入法也叫统计学家舍入('s )、无偏舍入( )。现在大部分编程软件的默认舍入都是银行家舍入法,比如 c/c++、、php、go,英特尔处理器用的也是银行家舍入。
规则四舍六入五取偶:
•(1)被修约的数字小于5时,该数字舍去;
•(2)被修约的数字大于5时,则进位;
•(3)被修约的数字等于5时,要看5前面的数字,若是奇数则进位,若是偶数则将5舍掉,即修约后末尾数字都成为偶数;若5的后面还有不为“0”的任何数,则此时无论5的前面是奇数还是偶数,均应进位。
示例:
•9.8249=9.82,9.82671=9.83
•9.835=9.84,9.8351 =9.84
•9.825=9.82,9.82501=9.83
简单的说,就是:四舍六入五考虑,五后非空就进一,五后为空看奇偶,五前为偶应舍去,五前为奇要进一
测试银行家舍入法正常情况:
// 5 后非 0 进位后舍去
const err1 = 22.4451
.log('err1', err1.(2)); // 22.45
// 5 后非 0 进位后舍去
const err1 = 22.
.log('err1', err1.(2)); // 22.45
// 5后无,5前一位为奇数 进位
const err1 = 22.475
.log('err1', err1.(2)); // 22.48
// 5后无,5前一位为偶数 舍去
const err1 = 22.485
.log('err1', err1.(2)); // 22.48
测试异常情况:
// 5后无,5前一位为奇数 进位
const err1 = 22.415
.log('err1', err1.(2)); // 22.41
// 5后无,5前一位为奇数 进位
const err1 = 22.455
.log('err1', err1.(2)); // 22.45
// 5后无,5前一位为偶数 舍去
const err1 = 22.445
.log('err1', err1.(2)); // 22.45
// 5后无,5前一位为偶数 舍去
const err1 = 22.425
.log('err1', err1.(2)); // 22.43
// 5后无,5前一位为偶数 舍去
const err1 = 22.405
.log('err1', err1.(2)); // 22.41
// 5后无,5前一位为奇数 进位
const err1 = 0.015
.log('err1', err1.(2)); // 0.01
注意:这个挺吓人的,0.015 变成 0.01 慎用啊
原因分析:
const err1 = 1.005
.log('err1', err1.(2)); // "1.00"(预期是 "1.01")
原因:浮点数在内存中的实际值可能略小于表面值。例如,`1.005` 实际存储为 `1.`,导致四舍五入错误。
可以用下面方法查询浮点数在内存中存的值
function getDecimalValue(floatNum) {
// 这里不用 Decimal.toBinary, 因为toString 更准确
const binaryString = '0b' + floatNum.toString(2) // 将小数转为二进制
// console.log(binaryString)
let decimalValue = new Decimal(binaryString, 2).toString(); // 直接从二进制转换并创建Decimal对象
console.log(decimalValue);
return Number(decimalValue);
}
然后重新测一下上面的值发现结果都遵循银行家舍入了。
const err1 = 22.415
// 22.90625
.log((err1).(2)); // 22.41
const err1 = 22.455
// 22.
.log((err1).(2)); // 22.45
const err1 = 22.445
// 22.
.log((err1).(2)); // 22.45
const err1 = 22.425
// 22.
.log((err1).(2)); // 22.43
const err1 = 22.425
// 22.
.log((err1).(2)); // 22.41
const err1 = 0.015
// 0.
.log((err1).(2)); // 0.01

但是浮点数有数度问题怎么办呢,本人觉得可以考虑其它方法,自己写一个方法或是用下面方法
使用正常四舍五入
看到网上有很多这么写的,其实是不对的,不加 ,这个结果是 1,没有 .00
function roundUp(num, digits) {
const factor = 10 ** digits;
return (Math.round(num * factor) / factor).toFixed(digits);
}
const test = 1.005
console.log(roundUp(test, 2));
上面结果还是 1.00 ,不是 1.01,还是有精度问题
因为 1.005 存储在内存中值是 1.
乘以 100 后是 100......... 四舍五入后还是 100 而不是 101,所以还是使用高精度库函数
也有用下面这种方法的,但是用 23.005 保留 2 位小数, 测试就不准了
function roundUp(num, digits) {
const factor = 10 ** digits
const roundAdd1 = Math.round(num * factor * 10)
const round = Math.round(roundAdd1 / 10)
return (round / factor).toFixed(digits)
}
Big.js
function roundUp(num, precision = 0) {
const bigNum = new Big(num).round(precision)
return bigNum.toNumber().valueOf()
}
const test = 1.005
console.log(new Big(test).toNumber().valueOf()); // 1.005
console.log(roundUp(test, 2)); // 1.01
.js
查看了源码,.js 的 默认用的是正常四舍五入
const num3 = 1.005;
console.log(new Decimal(num3).toFixed(2).toString());
如果需要配置银行家舍入法,可以进行设置
Decimal.set({ rounding: 5 })
或是
const Decimal9 = Decimal.clone({ rounding: 5 })
b = new Decimal9(1)
配置表,默认是 1 ()
tion
away from zero
zero
-
.
If , away from zero
.
If , zero
.
If , even
.
If ,
.
If , -
Not a mode, see
但是小数点后面位数过多时,高精度库存储也会有问题,这个可能是 使用64位双精度,由0或1组成64位,有效数位数只有52位,有效数部分无法存储无限长度的二进制,后面的数据就会丢失,计算机只能存储一个近似的数字。
尽管 .js 旨在提供比原生 数字类型更高的精度,但它仍然受到底层浮点数系统的限制。
const num3 = 1234567890.012345678;
console.log(new Decimal(num3).toString()); // 1234567890.0123458 























