Skip to content

[js] 第100天 分别封装精确运算的加减乘除四个方法 #981

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
haizhilin2013 opened this issue Jul 24, 2019 · 6 comments
Open
Labels
js JavaScript

Comments

@haizhilin2013
Copy link
Collaborator

第100天 分别封装精确运算的加减乘除四个方法

@haizhilin2013 haizhilin2013 added the js JavaScript label Jul 24, 2019
@ghost
Copy link

ghost commented Jul 25, 2019

只会整数运算,不会小数。

前几天刚练习用 C++ 写这东西,头疼就不再用 js 写一遍了。

基本上就是数组中每个成员存储一位,[0] 是个位,[1] 是十位,以此类推,然后计算时模拟竖式计算处理进位、借位。

除法太难不会,至少加减乘都还比较好写。

其实每个成员不止能存一位数字,因为数字 987654321987654321 既可以这样存:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9]

也可以这样存:

[987654321, 987654321]

计算时都是一样的。

但是这种压位数的操作要注意计算时不能溢出 Number.MAX_SAFE_INTEGER

ESNext BigInt 提案已经进入 Stage 3,哪天通过了就也不用写这些东西了

@forever-z-133
Copy link

forever-z-133 commented Jul 27, 2019

刚好最近有写过,用来处理小数计算时的精度问题。

function isObject(obj) {
  return Object.prototype.toString.call(obj) === '[object Object]';
}

/**
 * 专门处理数字运算
 * 并解决,1. 非数字型数字的运算 2. 小数计算的精度问题
 * count('+', 0.1, '0.2'); // 0.3
 */
function count(type, options) {
    var nums = [].slice.call(arguments, 2);
    var _startConfig = { '+': 0, '-': 0, '*': 1, '/': 1 };
    if (!(type in _startConfig)) return new Error('首位入参有误');

    // 可能往后会加入些配置,但如果不是对象则不是配置
    if (!isObject(options)) {
        nums.splice(0, 0, options);
    }

    // 小数点后面最长字符长度,比如 0.1 和 0.234 则返回 3
    var maxDotLength = nums.reduce(function(max, num) {
        return Math.max(max, ([num].toString().split('.')[1] || '').length);
    }, 0);

    // 改造成整数,并计算出结果,比如 0.1 + 0.2 改为 1+2
    var startNum = _startConfig[type];
    var pow = Math.pow(10, maxDotLength);
    var result = nums.reduce(function(re, num, index) {
        num = Number(num) * pow;
        if (type === '-' && index === 0) return num;
        switch (type) {
            case '-': return re - num;
            case '*': return re * num;
            case '/': return re / num;
            case '+': default: return re + num;
        }
    }, startNum);

    // 回退到原小数形态,比如 3 转为 0.3
    var _divideConfig = { '+': pow, '-': pow, '*': pow * pow, '/': 1 };
    result = result / _divideConfig[type];

    return result;
}

@ghost
Copy link

ghost commented Jul 27, 2019

@foreverZ133 你的写法可以处理小数的不精确问题,但是无法处理大整数导致的 Number.MAX_SAFE_INTEGER 溢出问题。对于这个问题,解决方案通常是拆成多段计算。

这里贴上一段最近写的处理大整数加法的 C++ 代码,抛砖引玉。

struct bigint {
    static const int limit = 10;
    
    int num[100003] { 0 };
    size_t size = 0;
    
    bigint() { bigint(0); }
    explicit bigint(unsigned long long i) {
        int pos = 0;
        while (i) {
            num[pos] = i % limit;
            pos++;
            size++;
            i /= 10;
        }
    }
    explicit bigint(const string &s) {
        size = s.size();
        for (int i = 0; i < s.size(); i++) {
            num[i] = s[size - i - 1];
        }
    }
    
    int &operator [](size_t rhs) {
        bigint &lhs = *this;
        return lhs.num[rhs];
    }
    int operator [](size_t rhs) const {
        const bigint &lhs = *this;
        return lhs.num[rhs];
    }
    
    bigint operator +(const bigint &rhs) const {
        const bigint &lhs = *this;
        size_t mlen = max(lhs.size, rhs.size);
        bigint result;
        for (size_t i = 0; i < mlen || result[i] >= limit; i++) {
            if (i < mlen)
                result[i] += lhs[i] + rhs[i];
            result.size = i + 1;
            if (lhs[i] + rhs[i] >= limit) {
                result.size++;
                result[i + 1] += result[i] / 10;
                result[i] = result[i] % 10;
            }
        }
        return result;
    }
};

@canwdev
Copy link

canwdev commented Feb 23, 2020

@songbing1990
Copy link

function Calculate() {

  // 获取小数位数
  const getMaxDigit = (x, y) => {
    const xStr = String(x)
    const yStr = String(y)

    const xIndex = xStr.indexOf('.')
    const yIndex = yStr.indexOf('.')

    return Math.max((xStr.length - xIndex + 1), (yStr.length - yIndex + 1))
  }

  const add = (x, y) => {
    let digit = getMaxDigit(x , y)
    let exponent = 1
    while (digit--) {
      exponent *= 10
    }
    return (x * exponent + y * exponent) / exponent
  }

  const subtract = () => {

  }

  const multiply = () => {

  }

  const divide = () => {

  }

  return {
    add,
    subtract,
    multiply,
    divide
  }
}

const { add } = new Calculate()

add(0.1, 0.2)

@smile-2008
Copy link

刚好最近有写过,用来处理小数计算时的精度问题。

function isObject(obj) {
  return Object.prototype.toString.call(obj) === '[object Object]';
}

/**
 * 专门处理数字运算
 * 并解决,1. 非数字型数字的运算 2. 小数计算的精度问题
 * count('+', 0.1, '0.2'); // 0.3
 */
function count(type, options) {
    var nums = [].slice.call(arguments, 2);
    var _startConfig = { '+': 0, '-': 0, '*': 1, '/': 1 };
    if (!(type in _startConfig)) return new Error('首位入参有误');

    // 可能往后会加入些配置,但如果不是对象则不是配置
    if (!isObject(options)) {
        nums.splice(0, 0, options);
    }

    // 小数点后面最长字符长度,比如 0.1 和 0.234 则返回 3
    var maxDotLength = nums.reduce(function(max, num) {
        return Math.max(max, ([num].toString().split('.')[1] || '').length);
    }, 0);

    // 改造成整数,并计算出结果,比如 0.1 + 0.2 改为 1+2
    var startNum = _startConfig[type];
    var pow = Math.pow(10, maxDotLength);
    var result = nums.reduce(function(re, num, index) {
        num = Number(num) * pow;
        if (type === '-' && index === 0) return num;
        switch (type) {
            case '-': return re - num;
            case '*': return re * num;
            case '/': return re / num;
            case '+': default: return re + num;
        }
    }, startNum);

    // 回退到原小数形态,比如 3 转为 0.3
    var _divideConfig = { '+': pow, '-': pow, '*': pow * pow, '/': 1 };
    result = result / _divideConfig[type];

    return result;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
js JavaScript
Projects
None yet
Development

No branches or pull requests

5 participants