math-round

431 阅读2分钟

_.round(number, [precision=0])

根据 precision(精度) 四舍五入 number

参数

  1. number  (number) : 要四舍五入的数字。
  2. [precision=0]  (number) : 四舍五入的精度。

返回

(number) : 返回四舍五入的数字。

例子

_.round(4.006);
// => 4
 
_.round(4.006, 2);
// => 4.01
 
_.round(4060, -2);
// => 4100

在一次bug中发现Math.toFixed的问题

chrome 上的测试结果:

1.35.toFixed(1); // 1.4 正确
1.335.toFixed(2); // 1.33 错误
1.3335.toFixed(3); // 1.333 错误
1.33335.toFixed(4); // 1.3334 正确
1.333335.toFixed(5); // 1.33333 错误
1.3333335.toFixed(6); // 1.333333 错误

对于5这个东西,跟日常的数字四舍五入不一致

常见修复方法

    function round(number, precision) {
        return Math.round(number * Math.pow(10, precision)) / Math.pow(10, precision)
    }

lodash源码


const round = createRound('round')
/**
 * Creates a function like `round`.
 *
 * @private
 * @param {string} methodName The name of the `Math` method to use when rounding.
 * @returns {Function} Returns the new round function.
 */
 
function createRound(methodName) {
  // Math.round
  const func = Math[methodName]
  return (number, precision) => {
      
    // 精度 -292 到 292  默认为0
    precision = precision == null ? 0 : (precision >= 0 ? Math.min(precision, 292) : Math.max(precision, -292))
    if (precision) {
      // 这里的逻辑就是Math.round(number * Math.pow(10, precision)) / Math.pow(10, precision)
      let pair = `${number}e`.split('e')
      const value = func(`${pair[0]}e${+pair[1] + precision}`)

      pair = `${value}e`.split('e')
      return +`${pair[0]}e${+pair[1] - precision}`
    }
    return func(number)
  }
}

export default createRound

最后

292 最大精度啥回事???

IEEE754 双精度规范 使用8个字节(64位进行存储),结构如下

  • 符号位 Sign(S) : 1bit (b63)
  • 指数部分Exponent(E) : 11bit (b62-b52)
  • 尾数部分Mantissa(M) : 52bit (b51-b0)
  • 表达式 -1^S * 2^E * 1.M ————————————————

1. 符号位说明

0为正数 1为负数

2. 部分指数理解

指数部分二进制值范围位0 ~ 2047(2^11-1),但这个取值范围并不能直接使用,需要减去一个偏移1023(2^10-1)(除了最高位其他全为1的时)。所以E的取值范围应该为-1022~1023,为啥不是-1023~1024呢?因为规定-1023和1024为特殊情况使用。

3. 尾数部分理解

尾数部分对应小数部分,占52bit。 0.11为2^E-2 + 2^-4。 小数部分最大取值为(1-2^-52)。

————————————————

这里就要引出js的几个常量

1. 最大值 Number.MAX_VALUE

指数部分和小数部分都取最大值时代入表达式:2^1023*(1+(1-2^-52))。

2. 最小值Number.MIN_VALUE

注意Number.MIN_VALUE时表示的正数最小值,而不是最小值。最小值应为-2^1023*(1+(1-2^-52))

3. 精度Number.EPSILON

两个数的最小差值即尾数部分最后一位是1其他全为0:2^-52。

4. 安全整数

Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER

需要安全表示,则需要保证精确,必须保证这个数转为的2进制必须在尾数部分中,不能四舍五入。一共有52位尾数能表示的整数是2^53-1,那么最大安全整数:2^53-1 最小则为:-(2^53-1)

console.log(Number.MAX_VALUE/Number.MAX_SAFE_INTEGER) 1.99584030953472e+292