如何避免对JavaScript中的数字进行科学计数?


182

当数字变大时,JavaScript会将大INT转换为科学计数法。如何防止这种情况发生?


1
您要1E21显示为“ 1000000000000000000000”吗?您是否担心数字的显示或存储方式?
2009年

3
我担心它的显示方式:我有一个document.write(myvariable)命令
chris

1
我需要将该数字作为URL的一部分
chris

3
@outis:并非只有用户想要阅读数字。似乎D3遇到translate包含科学计数法的坐标的转换时会抛出异常。
或Mapper

2
当您处理不超过一定大小的数字时,此方法仍有用途。对于根本也不了解科学记数法的用户而言,科学记数法将很难阅读。
hippietrail

Answers:


108

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;
}

上面使用便宜的'n'-简单字符串重复((new Array(n+1)).join(str))。您可以定义String.prototype.repeat使用俄罗斯农民乘法,而改为使用。

该答案仅应适用于以下问题:显示大量而不使用科学计数法。除此之外,您应该使用BigInt库,例如BigNumber,Leemon's BigIntBigInteger。展望未来,新的本地BigInt(注:不是Leemon's)应该可用;Chromium和基于它的浏览器(Chrome,新的Edge [v79 +],Brave)和Firefox均受支持;Safari的支持正在进行中。

这是使用BigInt的方法: BigInt(n).toString()

例:

不过请注意,作为JavaScript数字(不是BigInt)输出的任何大于15-16位(特别是大于Number.MAX_SAFE_INTEGER + 1[9,007,199,254,740,992])的整数都应四舍五入,因为JavaScript的数字类型(IEEE-754双精度)浮点数)不能精确地保存该点以外的所有整数。由于Number.MAX_SAFE_INTEGER + 1它以2的倍数工作,因此它不再容纳奇数(并且类似地,它以18,014,398,509,481,984开始以4的倍数工作,然后是8,然后是16,...)。

因此,如果您可以依靠BigInt支持,请将您的数字作为字符串输出,然后传递给该BigInt函数:

const n = BigInt("YourNumberHere");

例:


您的解决方案给我的2 ^ 1000结果与wolframalpha截然不同。有指针吗?
Shane Reustle,2011年

4
@Shane:此问题与解答有关将浮点数显示为以10为底的整数,并且不处理不能以浮点格式表示的数字(转换为10时会出现)。您需要一个JS bigint库,如最后一行所述。
2011年

1
@PeterOlson:好像我遗漏了一个Math.abs。感谢您的单挑。
outis 2012年

1
toFixed(Number.MAX_VALUE) == Number.MAX_VALUE应该返回true,但是不会返回
manonthemat

3
实际上,此代码实际上不适用于非常小的负数:toFixed(-1E-20)->“ 0.0000000000000000000.099999999999999999999”
davidthings

44

我知道这是一个比较老的问题,但是显示最近很活跃。MDN到LocaleString

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"

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

注意:

Number.toLocaleString()四舍五入到小数点后16位,因此...

const myNumb = 586084736227728377283728272309128120398;
console.log( myNumb.toLocaleString('fullwide', { useGrouping: false }) );

...返回...

586084736227728400000000000000000000000

如果精度对预期结果很重要,则这可能是不希望的。


2
这是万无一失的答案。
咏叹调

这在PhantomJS 2.1.1中不起作用-仍然以科学计数法结束。在Chrome中效果不错。
Egor Nepomnyaschih,

1
这对于很小的小数似乎无效:var myNumb = 0.0000000001; console.log(myNumb.toLocaleString('fullwide',{useGrouping:false}));
eh1160 '19

可以设置maximumSignificantDigits选项(最多21个)来格式化非常小的小数,即:js console.log( myNumb.toLocaleString('fullwide', { useGrouping: true, maximumSignificantDigits:6}) );
先生。MaNiAl19年

20

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

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

26
字面意思是要问大量数字的问题
swyx

1
请注意避免将0作为toFixed通话的参数-最终将导致删除尾随的零:(1000).toFixed(0).replace(/\.?0+$/,"") // 1, not 1000
Egor Nepomnyaschih,

15

另一种可能的解决方案:

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

7
这不会保留原始值...例如31415926535897932384626433832795变为31415926535897938480804068462624
zero_cool

不太乐观,但可能与JavaScript处理大量数字有关。
zero_cool

@domsson基本上是因为IEEE浮点数算法适用。Javascript中的数字实际上表示为浮点数以及“十进制数”,没有本机的“整数精确类型”。您可以在此处了解
Pac0

@zero_cool大于此数字的任何数字Number.MAX_SAFE_INTEGER都可能遇到此问题。
Pac0

10

这是我的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"


如果将Number.MAX_VALUE.toFixedSpecial(2)与Number.MAX_VALUE进行比较,则会发现它们不相等。
manonthemat

4
@manonthemat当然它们并不相等,因为第一个是格式化的字符串,第二个是a Number。如果将第一个强制转换为a Number,您将看到它们是绝对相等的:jsfiddle.net/qd6hpnyx/1。您可以收回反对意见:P
VisioN

足够公平....我只是注意到我做不到,除非您正在编辑答案。
manonthemat

最好也.0用空字符串替换任何结尾。
vsync

当我尝试时,我得到一个有点奇怪的值console.log(2.2e307.toFixedSpecial(10))。我的意思是...我得到几个尾随的零。无论如何都支持,因为这似乎最接近我的需求。
peter.petrov

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));


1
numberToString(0.0000002)“ 0.00000019999999999999998”
raphadko

1
@raphadko这就是臭名昭著的Javascript 浮点精度的问题,在JS ...查看使用数字时,例如这是另一种完全不同的疼痛stackoverflow.com/questions/1458633/...
user56reinstatemonica8

3

别人的答案没有给您确切的数字!
此函数可准确计算所需的数字,并将其返回到字符串中以防止被javascript更改!
如果需要数值结果,只需将函数的结果乘以第一!

function toNonExponential(value) {
    // if value is not a number try to convert it to number
    if (typeof value !== "number") {
        value = parseFloat(value);

        // after convert, if value is not a number return empty string
        if (isNaN(value)) {
            return "";
        }
    }

    var sign;
    var e;

    // if value is negative, save "-" in sign variable and calculate the absolute value
    if (value < 0) {
        sign = "-";
        value = Math.abs(value);
    }
    else {
        sign = "";
    }

    // if value is between 0 and 1
    if (value < 1.0) {
        // get e value
        e = parseInt(value.toString().split('e-')[1]);

        // if value is exponential convert it to non exponential
        if (e) {
            value *= Math.pow(10, e - 1);
            value = '0.' + (new Array(e)).join('0') + value.toString().substring(2);
        }
    }
    else {
        // get e value
        e = parseInt(value.toString().split('e+')[1]);

        // if value is exponential convert it to non exponential
        if (e) {
            value /= Math.pow(10, e);
            value += (new Array(e + 1)).join('0');
        }
    }

    // if value has negative sign, add to it
    return sign + value;
}

关于仅此代码答案的一些其他信息可能会很有用。
皮夫斯'17

添加到功能的注释。

2

使用.toPrecision.toFixed等等。您可以通过将数字转换为字符串.toString然后查看来计算数字中的位数 .length


由于某些原因,toPrecision不起作用。如果尝试:window.examplenum = 1352356324623461346,然后说出alert(window.examplenum.toPrecision(20)),它不会弹出警报
chris

实际上,有时弹出时会显示科学计数法,而有时根本不会弹出。我究竟做错了什么?
克里斯,

18
两种建议方法都不适用于大(或小)数字。(2e64).toString()将返回"2e+64",所以.length是没有用的。
CodeManX 2014年

2

这就是我最终用来从输入中获取值的方法,将小于17位的数字展开并将指数转换为x10 y

// e.g.
//  niceNumber("1.24e+4")   becomes 
// 1.24x10 to the power of 4 [displayed in Superscript]

function niceNumber(num) {
  try{
        var sOut = num.toString();
      if ( sOut.length >=17 || sOut.indexOf("e") > 0){
      sOut=parseFloat(num).toPrecision(5)+"";
      sOut = sOut.replace("e","x10<sup>")+"</sup>";
      }
      return sOut;

  }
  catch ( e) {
      return num;
  }
}

1

您可以遍历数字并实现舍入

//在给定索引处替换char的功能

String.prototype.replaceAt=function(index, character) {
    return this.substr(0, index) + character + this.substr(index+character.length);
}

//循环开始

var str = "123456789123456799.55";
var arr = str.split('.');
str = arr[0];
i = (str.length-1);
if(arr[1].length && Math.round(arr[1]/100)){
  while(i>0){
    var intVal = parseInt(str.charAt(i));

   if(intVal == 9){
      str = str.replaceAt(i,'0');
      console.log(1,str)
   }else{
      str = str.replaceAt(i,(intVal+1).toString()); 
      console.log(2,i,(intVal+1).toString(),str)
      break;
   }
   i--;
 }
}

1

我尝试使用字符串形式而不是数字,这似乎可行。我只在Chrome上测试过此功能,但应该是通用的:

function removeExponent(s) {
    var ie = s.indexOf('e');
    if (ie != -1) {
        if (s.charAt(ie + 1) == '-') {
            // negative exponent, prepend with .0s
            var n = s.substr(ie + 2).match(/[0-9]+/);
            s = s.substr(2, ie - 2); // remove the leading '0.' and exponent chars
            for (var i = 0; i < n; i++) {
                s = '0' + s;
            }
            s = '.' + s;
        } else {
            // positive exponent, postpend with 0s
            var n = s.substr(ie + 1).match(/[0-9]+/);
            s = s.substr(0, ie); // strip off exponent chars            
            for (var i = 0; i < n; i++) {
                s += '0';
            }       
        }
    }
    return s;
}

1

我认为可能会有几个类似的答案,但这是我想出的一件事

// If you're gonna tell me not to use 'with' I understand, just,
// it has no other purpose, ;( andthe code actually looks neater
// 'with' it but I will edit the answer if anyone insists
var commas = false;

function digit(number1, index1, base1) {
    with (Math) {
        return floor(number1/pow(base1, index1))%base1;
    }
}

function digits(number1, base1) {
    with (Math) {
        o = "";
        l = floor(log10(number1)/log10(base1));
        for (var index1 = 0; index1 < l+1; index1++) {
            o = digit(number1, index1, base1) + o;
            if (commas && i%3==2 && i<l) {
                o = "," + o;
            }
        }
        return o;
    }
}

// Test - this is the limit of accurate digits I think
console.log(1234567890123450);

注意:这仅与javascript数学函数一样准确,并且在for循环之前的行中使用log而不是log10时会出现问题;它将以10为基数的1000写为000,所以我将其更改为log10,因为无论如何人们都会使用以10为基数的。

这可能不是一个非常准确的解决方案,但是我很自豪地说它可以成功地跨基数转换数字,并带有逗号选项!


1

你的问题:

number :0x68656c6c6f206f72656f
display:4.9299704811152646e+23

您可以使用此:https : //github.com/MikeMcl/bignumber.js

一个用于任意精度十进制和非十进制算术的JavaScript库。

像这样:

let ten =new BigNumber('0x68656c6c6f206f72656f',16);
console.log(ten.toString(10));
display:492997048111526447310191

好吧,请注意,bignumber.js不能超过15个有效数字...
Michael Heuberger

0

我知道已经过了很多年,但是最近我一直在研究类似的问题,所以我想发布自己的解决方案。当前接受的答案用0填充指数部分,而我试图找到确切的答案,尽管由于JS对浮点精度的限制,通常对于很大的数字来说,它并不是十分准确。

确实适用Math.pow(2, 100),返回正确的值1267650600228229401401496703205376。

function toFixed(x) {
  var result = '';
  var xStr = x.toString(10);
  var digitCount = xStr.indexOf('e') === -1 ? xStr.length : (parseInt(xStr.substr(xStr.indexOf('e') + 1)) + 1);
  
  for (var i = 1; i <= digitCount; i++) {
    var mod = (x % Math.pow(10, i)).toString(10);
    var exponent = (mod.indexOf('e') === -1) ? 0 : parseInt(mod.substr(mod.indexOf('e')+1));
    if ((exponent === 0 && mod.length !== i) || (exponent > 0 && exponent !== i-1)) {
      result = '0' + result;
    }
    else {
      result = mod.charAt(0) + result;
    }
  }
  return result;
}

console.log(toFixed(Math.pow(2,100))); // 1267650600228229401496703205376


@Oriol-是的,你是对的;我想我以前尝试过的任何方法由于其他因素只能通过这种方式解决。我将在回答中删除该小笔记。
andi

1
不起作用:toFixed(Number.MAX_VALUE) == Number.MAX_VALUE
manonthemat

0
function printInt(n) { return n.toPrecision(100).replace(/\..*/,""); }

有一些问题:

  • 0.9显示为“ 0”
  • -0.9显示为“ -0”
  • 1e100显示为“ 1”
  • 仅适用于〜1e99以下的数字=>对更大的数字使用其他常数;或更小以进行优化。

是的,这是一些问题。
亚当·莱格特

0

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

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));


-1

如果您只是为了显示而已,则可以在四舍五入之前用数字构建一个数组。

var num = Math.pow(2, 100);
var reconstruct = [];
while(num > 0) {
    reconstruct.unshift(num % 10);
    num = Math.floor(num / 10);
}
console.log(reconstruct.join(''));

这将返回正确的答案,但是会花费很多时间,并且可能最终导致执行超时。
汉娜

-1

当前,尚无本机功能来解决科学符号。但是,为此,您必须编写自己的功能。

这是我的:

function dissolveExponentialNotation(number)
{
    if(!Number.isFinite(number)) { return undefined; }

    let text = number.toString();
    let items = text.split('e');

    if(items.length == 1) { return text; }

    let significandText = items[0];
    let exponent = parseInt(items[1]);

    let characters = Array.from(significandText);
    let minus = characters[0] == '-';
    if(minus) { characters.splice(0, 1); }
    let indexDot = characters.reduce((accumulator, character, index) =>
    {
        if(!accumulator.found) { if(character == '.') { accumulator.found = true; } else { accumulator.index++; } }
        return accumulator;
    }, { index: 0, found: false }).index;

    characters.splice(indexDot, 1);

    indexDot += exponent;

    if(indexDot >= 0 && indexDot < characters.length - 1)
    {
        characters.splice(indexDot, 0, '.');
    }
    else if(indexDot < 0)
    {
        characters.unshift("0.", "0".repeat(-indexDot));
    }
    else
    {
        characters.push("0".repeat(indexDot - characters.length));
    }

    return (minus ? "-" : "") + characters.join("");
}

对于很大的数字,这将失败。我尝试了2830869077153280552556547081187254342445169156730并得到2830869077153280500000000000000000000000000000000000000
回调

-1

您可以使用从指数模块。它轻巧且经过全面测试。

import fromExponential from 'from-exponential';

fromExponential(1.123e-10); // => '0.0000000001123'

-1

您也可以使用YourJS.fullNumber。例如,YourJS.fullNumber(Number.MAX_VALUE)结果如下: 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

它也适用于非常小的数字。 YourJS.fullNumber(Number.MIN_VALUE)返回此: 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005

重要的是要注意,此函数将始终以字符串形式返回有限数字,但将以形式返回非有限数字(例如NaNInfinityundefined

您可以在YourJS控制台中对其进行测试。


-3

您可以使用number.toString(10.1)

console.log(Number.MAX_VALUE.toString(10.1));

注意:此功能目前在Chrome浏览器中有效,但在Firefox中不可用。规范说基数必须是整数,因此这会导致行为不可靠。


这到底是做什么的?基数为10.1?
joniba

这行不通。从ES5开始,实现需要在参数上使用ToInteger。因此,使用10.110或没有什么是等效的。
Oriol

是的,这确实在最近停止了工作。金达很伤心,但是这个答案现在无关紧要。
Zambonifofex

-6

oracle返回科学符号时也遇到了同样的问题,但是我需要URL的实际数字。我只是通过减去零来使用PHP技巧,然后得到了正确的数字。

例如5.4987E7是val。

newval = val - 0;

newval现在等于54987000


2
这没有作用。5.4987E7的指数小于21,因此无论何时转换为字符串,它都显示为整数。(5.4987E21-0).toString()=>“ 5.4987e + 21”
Dwight
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.