如何避免JavaScript中大数字的科学记数法?

chr*_*ris 161 javascript

当数字变大时,JavaScript会将大型INT转换为科学记数法.我怎样才能防止这种情况发生?

out*_*tis 102

Number.toFixed,但它使用科学记数法,如果数字> = 1e21并且最大精度为20.除此之外,你可以自己滚动,但它会很乱.

function toFixed(x) {
  if (Math.abs(x) < 1.0) {
    var e = parseInt(x.toString().split('e-')[1]);
    if (e) {
        x *= Math.pow(10,e-1);
        x = '0.' + (new Array(e)).join('0') + x.toString().substring(2);
    }
  } else {
    var e = parseInt(x.toString().split('+')[1]);
    if (e > 20) {
        e -= 20;
        x /= Math.pow(10,e);
        x += (new Array(e+1)).join('0');
    }
  }
  return x;
}
Run Code Online (Sandbox Code Playgroud)

上面使用便宜的''n'-easy字符串重复((new Array(n+1)).join(str)).您可以String.prototype.repeat使用俄语Peasant Multiplication 定义并使用它.

这个答案应该只适用于问题的背景:不使用科学记数法显示大量数字.除此之外,您应该使用BigInt库,例如BigNumber,Leemon的BigIntBigInteger.展望未来,应该原生支持一个提议的BigInt(注意:不是Leemon的)(Chrome有支持,而且它正在为Firefox工作).

  • @Shane:此Q&A是关于将浮点数显示为基数为10的整数,并不解决无法以浮点格式表示的数字(转换为基数10时会出现).你需要一个[JS bigint](http://stackoverflow.com/questions/3072307/is-there-a-bignum-library-for-javascript)库,如最后一行所述. (4认同)
  • 实际上这个代码不适用于非常小的负数:toFixed(-1E-20) - >"0.0000000000000000000.09999999999999999" (3认同)
  • 对于 2^1000,你的解决方案给了我一个与 Wolframalpha 非常不同的结果。有什么指点吗? (2认同)
  • 这对负数不起作用; 例如`-1e56`. (2认同)

fla*_*k17 27

我知道这是一个较老的问题,但最近表现活跃.MDN toLocaleString

const myNumb = 1000000000000000000000;
console.log( myNumb ); // 1e+21
console.log( myNumb.toLocaleString() ); // "1,000,000,000,000,000,000,000"
console.log( myNumb.toLocaleString('fullwide', {useGrouping:false}) ); // "1000000000000000000000"
Run Code Online (Sandbox Code Playgroud)

您可以使用选项格式化输出.

注意:

Number.toLocaleString()在16个小数位后舍入,这样......

const myNumb = 586084736227728377283728272309128120398;
console.log( myNumb.toLocaleString('fullwide', { useGrouping: false }) );
Run Code Online (Sandbox Code Playgroud)

... ...返回

586084736227728400000000000000000000000

如果准确性对于预期结果是重要的,则这可能是不期望的.

  • 这似乎不适用于非常小的小数: var myNumb = 0.0000000001; console.log( myNumb.toLocaleString('fullwide', { useGrouping: false }) ); (4认同)
  • 16 位之后的舍入不是“toLocaleString()”限制;这是“数量”限制。`数字.MAX_SAFE_INTEGER = 9007199254740991` (4认同)
  • 根据用户的区域设置,使用“fullwide”可能会产生不稳定的结果。不同的区域设置可以使用句点或逗号作为分隔符。使用版本 `toLocaleString('en-US', { useGrouping: false, MaximumFractionDigits: 20 })` 不是更好吗? (4认同)
  • 这是万无一失的答案。 (2认同)
  • 可以设置 MaximumSignificantDigits 选项(最多 21)来格式化非常小的小数,即: ```js console.log( myNumb.toLocaleString('fullwide', { useGrouping: true, MaximumSignificantDigits:6}) ); ```` (2认同)

小智 18

对于较小的数字,并且您知道您想要多少小数,您可以使用toFixed,然后使用正则表达式删除尾随零.

Number(1e-7).toFixed(8).replace(/\.?0+$/,"") //0.000
Run Code Online (Sandbox Code Playgroud)

  • 问题字面上要求大量 (17认同)
  • 小心避免 0 作为 toFixed 调用的参数 - 它最终会擦除重要的尾随零:`(1000).toFixed(0).replace(/\.?0+$/,"") // 1,不是 1000` (2认同)

Mir*_*dil 16

一个可能的解决方案:

function toFix(i){
 var str='';
 do{
   let a = i%10;
   i=Math.trunc(i/10);
   str = a+str;
 }while(i>0)
 return str;
}
Run Code Online (Sandbox Code Playgroud)

  • 这不保留原始值...例如31415926535897932384626433832795变为31415926535897938480804068462624 (7认同)

Vis*_*ioN 11

这是我的Number.prototype.toFixed方法的简短变体,适用于任何数字:

Number.prototype.toFixedSpecial = function(n) {
  var str = this.toFixed(n);
  if (str.indexOf('e+') === -1)
    return str;

  // if number is in scientific notation, pick (b)ase and (p)ower
  str = str.replace('.', '').split('e+').reduce(function(p, b) {
    return p + Array(b - p.length + 2).join(0);
  });
  
  if (n > 0)
    str += '.' + Array(n + 1).join(0);
  
  return str;
};

console.log( 1e21.toFixedSpecial(2) );       // "1000000000000000000000.00"
console.log( 2.1e24.toFixedSpecial(0) );     // "2100000000000000000000000"
console.log( 1234567..toFixedSpecial(1) );   // "1234567.0"
console.log( 1234567.89.toFixedSpecial(3) ); // "1234567.890"
Run Code Online (Sandbox Code Playgroud)

  • @manonthemat当然它们并不相同,因为第一个是格式化的**字符串**,第二个是`Number`.如果你将第一个转换为`Number`,你会看到它们*绝对*相等:https://jsfiddle.net/qd6hpnyx/1/.你可以把你的downvote回到`:P` (4认同)

Ada*_*ett 10

破坏正则表达式。这没有精度问题,也不是很多代码。

function toPlainString(num) {
  return (''+ +num).replace(/(-?)(\d*)\.?(\d*)e([+-]\d+)/,
    function(a,b,c,d,e) {
      return e < 0
        ? b + '0.' + Array(1-e-c.length).join(0) + c + d
        : b + c + d + Array(e-d.length+1).join(0);
    });
}

console.log(toPlainString(12345e+12));
console.log(toPlainString(12345e+24));
console.log(toPlainString(-12345e+24));
console.log(toPlainString(12345e-12));
console.log(toPlainString(123e-12));
console.log(toPlainString(-123e-12));
console.log(toPlainString(-123.45e-56));
console.log(toPlainString('1e-8'));
console.log(toPlainString('1.0e-8'));
Run Code Online (Sandbox Code Playgroud)

  • 这适用于带引号的普通数字、不带引号的普通数字、带引号的科学记数法和不带引号的科学记数法。 (3认同)
  • @MohsenAlyafei `(-0).toString()` 返回 `"0"`,我不想在这里扩展科学记数法。这需要显式测试“-0”或使用 toLocaleString/Intl.NumberFormat。 (2认同)

Jen*_*lly 6

以下解决方案绕过了非常大和非常小的数字的自动指数格式.这是带有错误修正的outis的解决方案:它不适用于非常小的负数.

function numberToString(num)
{
    let numStr = String(num);

    if (Math.abs(num) < 1.0)
    {
        let e = parseInt(num.toString().split('e-')[1]);
        if (e)
        {
            let negative = num < 0;
            if (negative) num *= -1
            num *= Math.pow(10, e - 1);
            numStr = '0.' + (new Array(e)).join('0') + num.toString().substring(2);
            if (negative) numStr = "-" + numStr;
        }
    }
    else
    {
        let e = parseInt(num.toString().split('+')[1]);
        if (e > 20)
        {
            e -= 20;
            num /= Math.pow(10, e);
            numStr = num.toString() + (new Array(e + 1)).join('0');
        }
    }

    return numStr;
}

// testing ...
console.log(numberToString(+0.0000000000000000001));
console.log(numberToString(-0.0000000000000000001));
console.log(numberToString(+314564649798762418795));
console.log(numberToString(-314564649798762418795));
Run Code Online (Sandbox Code Playgroud)


shr*_*pne 6

您可以使用from-exponential模块。它重量轻且经过充分测试。

import fromExponential from 'from-exponential';

fromExponential(1.123e-10); // => '0.0000000001123'
Run Code Online (Sandbox Code Playgroud)


Moh*_*fei 5

帖子的问题是避免使用 e 符号数字并将数字作为普通数字。

因此,如果只需要在不损失精度的情况下将 e(科学)记数法数字转换为普通数字(包括小数)那么就必须避免使用Math对象和其他javascript 数字方法,以便处理大数和大分数时不会发生舍入(由于二进制格式的内部存储总是发生这种情况)。

以下函数将 e(科学)记数法数字转换为普通数字(包括分数),同时处理大数和大分数而不会损失准确性,因为它不使用内置数学和数字函数来处理或操作数字。

该函数还处理普通数字,因此可以将怀疑变成“e”符号的数字传递给函数进行修复。

该函数应该使用不同的语言环境小数点。

提供了 94 个测试用例。

对于大的电子符号数字,将数字作为字符串传递。

例子:

eToNumber("123456789123456789.111122223333444455556666777788889999e+50");
// output:
"12345678912345678911112222333344445555666677778888999900000000000000"
Run Code Online (Sandbox Code Playgroud)
eToNumber("123.456123456789123456895e-80");
// output:
"0.00000000000000000000000000000000000000000000000000000000000000000000000000000123456123456789123456895"
Run Code Online (Sandbox Code Playgroud)
eToNumber("123456789123456789.111122223333444455556666777788889999e-50");
// output:
"0.00000000000000000000000000000000123456789123456789111122223333444455556666777788889999"
Run Code Online (Sandbox Code Playgroud)

Javascript 中有效的电子符号编号包括以下内容:

123e1   ==> 1230
123E1   ==> 1230
123e+1  ==> 1230
123.e+1 ==> 1230
123e-1  ==> 12.3
0.1e-1  ==> 0.01
.1e-1   ==> 0.01
-123e1  ==> -1230
Run Code Online (Sandbox Code Playgroud)

eToNumber("123456789123456789.111122223333444455556666777788889999e+50");
// output:
"12345678912345678911112222333344445555666677778888999900000000000000"
Run Code Online (Sandbox Code Playgroud)