Skip to content
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

jspref 比较 #12

Open
loskael opened this issue Aug 7, 2017 · 13 comments
Open

jspref 比较 #12

loskael opened this issue Aug 7, 2017 · 13 comments

Comments

@loskael
Copy link

loskael commented Aug 7, 2017

https://jsperf.com/calc-add

@loskael
Copy link
Author

loskael commented Aug 7, 2017

说明一下,one by one 这个 case 实在是太慢了,我把 counts 改成了 1e3,结果...

留一个问题:为什么 add 1 的 substr 是 -14

@loskael
Copy link
Author

loskael commented Aug 7, 2017

再提一点,add1 可以用一个 while 搞定

@miniflycn
Copy link
Member

叼叼叼,膜拜大佬

@miniflycn
Copy link
Member

话说有同学回答一下-14的问题,和写一个单个while就搞定的算法吗?

@kisscancer
Copy link

感觉应该改成-15会比较合适,js最大不会丢失进度的+运算,测试了一下是15位,见下图:

image

@changyu496
Copy link

changyu496 commented Aug 7, 2017

刚才自己跑了一遍代码,才发现自己在群里说的是错的(捂脸,下次不敢没跑代码就乱说了)
我现在理解这个-14就是一次分组,改变一位一位相加,那么为了不丢失精度,肯定越大越好了
我这边试过了两个15位的也是不会丢失精度
额,赞上面的同学,把例子写出来了!向你学习!

@moux1024
Copy link

moux1024 commented Aug 7, 2017

虽然

The JavaScript number format allows you to exactly represent all integers between
−9007199254740992 and 9007199254740992

我改成-15试了下,结果和-14是不一样的

let times = 100;
  let numbers1 = '';
  let numbers2 = '';
  while(--times) {
    numbers1 += Math.random();numbers2 += Math.random();
  }
  numbers1 = numbers1.replace(/\D/g, '');numbers2 = numbers2.replace(/\D/g, '');
const add1 = (x, y) => {
    let rs = [];
    
    while (x.length > 0 || y.length > 0) {
      rs.push(parseInt(x.substr(-14) || 0, 10) + parseInt(y.substr(-14) || 0, 10));
      x = x.substr(0, x.length - 14);
      y = y.substr(0, y.length - 14);
    }
  
    var u = 0, o = '';
    while (rs.length) {
      let v = (rs.shift() + u).toString();
      o = v.substr(-14) + o;
      u = parseInt(v.substr(0, v.length - 14) || 0, 10);
    }
  
    return o;
  }
const add2 = (x, y) => {
    let rs = [];
    
    while (x.length > 0 || y.length > 0) {
      rs.push(parseInt(x.substr(-15) || 0, 10) + parseInt(y.substr(-15) || 0, 10));
      x = x.substr(0, x.length - 15);
      y = y.substr(0, y.length - 15);
    }
  
    var u = 0, o = '';
    while (rs.length) {
      let v = (rs.shift() + u).toString();
      o = v.substr(-15) + o;
      u = parseInt(v.substr(0, v.length - 15) || 0, 10);
    }
  
    return o;
  }
console.log(add1(numbers1,numbers2));
console.log(add2(numbers1,numbers2));

@kangschampagne
Copy link

数字类型通过8字节的 double 浮点型表示,在JS中,使用 Number.MAX_SAFE_INTEGER 可以获得JS最大安全可进行算数运算的最大值范围 => 9007199254740991 十六位整型,因为使用的是浮点数运算会丢失精度,所以大于这个值运算都不对。 由于在第-14位上面有进位的问题,所以按13位进行切分计算,用-15会丢位的。

@kangschampagne
Copy link

const add1 = (x, y) => {
let rs = [];
let res = ''
while (x.length > 0 || y.length > 0) {
var a = parseInt(x.substr(-14) || 0, 10)
var b = parseInt(y.substr(-14) || 0, 10)
x = x.substr(0, x.length - 14);
y = y.substr(0, y.length - 14);
res = (a + b) + res
}
return res
}

@kisscancer
Copy link

666666~
我这个菜鸟基础还是太薄弱啦,要努力努力啦~

@loskael
Copy link
Author

loskael commented Aug 8, 2017

@moux1024

不一致是因为有bug,有一定概率会遇到, 我改了一个 split 的版本

const add2 = (x, y) => {
  x = '' + x;
  y = '' + y;

  if (/\D/.test((x + y))) {
    return NaN;
  }

  let output = '';
  let carry = '';
  let zero = '0000000000000000000000';

  const split = (str) => (zero + str).split(/(?=\d{14}$)/);
  const remove_left_zero = (str) => ('' + str).replace(/^0+/, '');

  while (x.length > 0 || y.length > 0 || carry.length > 0) {
    let tx = split(x);
    let ty = split(y);
    let ta = split(parseInt(tx[1] || 0, 10) + parseInt(ty[1] || 0, 10) + parseInt(carry || 0, 10));
    output = ta[1] + output;
    carry = ta[0];
    x = remove_left_zero(tx[0]);
    y = remove_left_zero(ty[0]);
    carry = remove_left_zero(carry);
  }

  return remove_left_zero(output);
}

@chencye
Copy link

chencye commented Aug 9, 2017

var safeLength = 15; // 14 ?
const add1 = (x, y) => {
    var sum = ''; // 相加结果
    var flag = 0; // 进位
    while (x.length > 0 || y.length > 0) {
        // 计算最后面safeLength个数字的相加值
        var tailX = x.substr(-safeLength) || '0';
        var tailY = y.substr(-safeLength) || '0';
        var tailSum = (parseInt(tailX) + parseInt(tailY) + flag).toString();
        // 前头补齐0(刚好切害到0开头的情况)
        var orginLength = Math.max(tailX.length, tailY.length);
        if (tailSum.length < orginLength) {
            tailSum = '0'.repeat(orginLength - tailSum.length) + tailSum;
        }
        // 重新计算进位
        flag = parseInt(tailSum.substr(0, tailSum.length - safeLength) || 0);
        // 除进位之外,添加到结果前面
        sum = tailSum.substr(-safeLength) + sum;
        // 去掉已计算的最后面safeLength个数字
        x = x.substr(0, x.length - safeLength);
        y = y.substr(0, y.length - safeLength);
    }
    // 最后的进位
    sum = flag ? flag + sum : sum;
    return sum;
}

function add2(a, b) {
    var aList = a.split('').reverse();
    var bList = b.split('').reverse();
    var max = Math.max(aList.length, bList.length);
    var cList = [];
    var flag = 0;
    
    for (var i = 0; i < max; i++) {
        var temp = (+aList[i] || 0) + (+bList[i] || 0) + flag;
        flag = 0;
        if (temp > 9) {
            temp -= 10;
            flag = 1;
        }
        cList.push(temp);
    }
    if (flag > 0) {
        cList.push(flag);
    }
    return cList.reverse().join('');
}

(function test() {
    let times = 100;
    let numbers = '';
    while(--times) {
        numbers += Math.random();
    }
    numbers = numbers.replace(/\D/g, '');
      
    let counts = 1e5;
    while(--counts) {
        let x = numbers.substring(Math.random() * 10 >> 0, Math.random() * 30 >> 0);
        let y = numbers.substring(Math.random() * 30 >> 0, Math.random() * 80 >> 0);
        //x = '1736';
        //y = '611111750505538122824820966007687248173708494041043351';
        var sum1 = add1(x, y);
        var sum2 = add2(x, y);
        if (sum1 != sum2) {
            console.log('x:' + x);
            console.log('y:' + y);
            console.log('sum1', sum1);
            console.log('sum2', sum2);
        }
    }
})()

@juntingl
Copy link

juntingl commented Jan 25, 2018

重来没有考虑过这种运算边界值的问题,还有很多要学啊~膜拜上面的各位大佬!

@miniflycn miniflycn reopened this Jul 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants