js精度问题

307 阅读2分钟

概述

image.png

解决方案

1、jsbi库

// https://github.com/GoogleChromeLabs/jsbi 
import JSBI from 'jsbi'; 
export const compute = (left: number | undefined, right: number | undefined) => { 
    return ( 
        JSBI.toNumber( 
            JSBI.add( 
                JSBI.BigInt(((left || 0) * 10000).toFixed(0)), 
                JSBI.BigInt(((right || 0) * 10000).toFixed(0)) ) ) / 10000 );
}; 
                
export const bigIntDivide = (left: number, right: number): number => { 
    return ( 
        JSBI.toNumber(
            JSBI.divide(JSBI.BigInt((left * 10000).toFixed(0)), JSBI.BigInt(right))) / 10000 ); }; 

export const bigIntMultiply = (left: number, right: number): number => { return ( JSBI.toNumber(JSBI.multiply(JSBI.BigInt((left * 10000).toFixed(0)), JSBI.BigInt(right))) / 10000 ); };

2、自定义函数


/**
 * 乘法函数,用来得到精确的乘法结果
 */
export const accMul = (arg1: number = 0, arg2: number = 0) => {
  let m = 0;
  const s1 = arg1.toString();
  const 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);
};

/**
 * 除法函数,用来得到精确的除法结果
 */
export const accDiv = (arg1: number = 0, arg2: number = 0) => {
  let t1 = 0;
  let t2 = 0;

  try {
      t1 = arg1.toString().split('.')[1].length;
  } catch (e) {}

  try {
      t2 = arg2.toString().split('.')[1].length;
  } catch (e) {}

  const r1 = Number(arg1.toString().replace('.', ''));
  const r2 = Number(arg2.toString().replace('.', ''));

  return accMul((r1 / r2), Math.pow(10, t2 - t1));
};

/**
* 加法函数,用来得到精确的加法结果
*/
export const accAdd = (arg1: number = 0, arg2: number = 0) => {
  let r1 = 0;
  let r2 = 0;

  try {
      r1 = arg1.toString().split('.')[1].length;
  } catch (e) {}

  try {
      r2 = arg2.toString().split('.')[1].length;
  } catch (e) {}

  const m = Math.pow(10, Math.max(r1, r2));

  return (accMul(arg1, m) + accMul(arg2, m)) / m;
};

/**
* 减法函数,用来得到精确的减法结果
*/
export const accSub = (arg1: number = 0, arg2: number = 0) => {
  let r1 = 0;
  let r2 = 0;

  try {
      r1 = arg1.toString().split('.')[1].length;
  } catch (e) {}

  try {
      r2 = arg2.toString().split('.')[1].length;
  } catch (e) {}

  const m = Math.pow(10, Math.max(r1, r2));
  const n = r1 >= r2 ? r1 : r2;

  return Number(((accMul(arg1, m) - accMul(arg2, m)) / m).toFixed(n));
};


原因

    • “下面再以 0.1 例解释浮点误差的原因, 0.1 转成二进制表示为 0.0001100110011001100(1100循环),1.100110011001100x2^-4,”
    • 这里进行了左移
    • 回到 JS 的实现中, 底层使用二进制存储数据, 数据格式为: IEEE-754, 用 64 bits 的空间存储数据, 由于某些原因, 使用 52bits 的空间存储有效数字, 但是实际存储了 52 + 1 = 53bits 的有效数字信息(因为第 1 个有效数字始终是 1, 不需要存储), 11 bits 的指数位信息.

      那么它们各自的取值范围是多少呢?

      有效数字: [0, 2^53 - 1] 指数位: [-1022, 1023] , 指数位还要考虑负指数的情况, 所以使用的是补码形式存储的数据, 还有一些其他的原因. 导致最终的结果不是[0, 2047]

  • blog.csdn.net/moguzhale/a…
  • 在线进制转化:tool.oschina.net/hexconvert/