Como evitar notação científica para grandes números em JavaScript?


183

JavaScript converte uma INT grande em notação científica quando o número se torna grande. Como posso impedir que isso aconteça?


1
Deseja que 1E21 seja exibido como '1000000000000000000000'? Você está preocupado com a forma como o número é exibido ou como é armazenado?
— Outis '06/

3
Estou preocupado com como ele é exibido: Eu tenho um document.write comando (myVariable)
— chris

1
eu preciso o número como parte de uma URL
— chris

3
@outis: usuários humanos não são os únicos que querem ler números. Parece que D3 lançará uma exceção ao encontrar uma translatetransformação que contenha coordenadas em notação científica.
— OR Mapper

2
Ainda há um uso para isso quando você está lidando com números até um determinado tamanho. A notação científica será difícil de ler para usuários que também não conhecem a notação científica.
— Hippietrail 15/09/19

Respostas:


110

Há Number.toFixed , mas ele usa notação científica se o número for> = 1e21 e tiver uma precisão máxima de 20. Fora isso, você pode rolar sozinho, mas será uma bagunça.

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

Acima usa a repetição de cordas barata e fácil ( (new Array(n+1)).join(str)). Você pode definir o String.prototype.repeatuso da multiplicação camponesa russa e usá-la.

Esta resposta deve ser aplicada apenas ao contexto da pergunta: exibir um número grande sem usar notação científica. Para qualquer outra coisa, você deve usar uma biblioteca BigInt , como BigNumber , BigInt do Leemon ou BigInteger . No futuro, o novo BigInt nativo (nota: não o Leemon) deve estar disponível; Chromium e navegadores baseados nele ( Chrome , o novo Edge [v79 +], Brave ) e Firefox têm suporte; O suporte do Safari está em andamento.

Veja como você usaria o BigInt para isso: BigInt(n).toString()

Exemplo:

Porém, tenha em atenção que qualquer número inteiro emitido como um número JavaScript (não um BigInt) com mais de 15 a 16 dígitos (especificamente, maior que Number.MAX_SAFE_INTEGER + 1[9.007.199.254.740.992]) pode ser arredondado, porque o tipo de número do JavaScript (IEEE-754 de dupla precisão ponto flutuante) não pode conter com precisão todos os números inteiros além desse ponto. Como Number.MAX_SAFE_INTEGER + 1está trabalhando em múltiplos de 2, não pode mais conter números ímpares (e, similarmente, em 18.014.398.509.481.984, ele começa a trabalhar em múltiplos de 4, depois 8 e 16, ...).

Conseqüentemente, se você puder contar com o BigIntsuporte, insira seu número como uma string que você passa para a BigIntfunção:

const n = BigInt("YourNumberHere");

Exemplo:


Sua solução me fornece um resultado muito diferente para 2 ^ 1000 do que wolframalpha. Alguma dica?
— Shane Reustle

4
@ Shane: Esta sessão de perguntas e respostas é sobre a exibição de números de ponto flutuante como números inteiros de base 10 e não trata de números que não podem ser representados em um formato de ponto flutuante (que ocorrerá ao converter para a base 10). Você precisa de uma biblioteca JS bigint , conforme mencionado na linha final.
— outis

1
@ PeterOlson: parece que eu deixei de fora a Math.abs. Obrigado pelo aviso.
— out 23/03

1
toFixed(Number.MAX_VALUE) == Number.MAX_VALUEdeve retornar verdadeiro então, mas não ...
— manonthemat

3
Na verdade, esse código, como está, não funciona para números negativos muito pequenos: toFixed (-1E-20) -> "0.000000000000000000000000.09999999999999999"
— davidthings 15/17

44

Eu sei que esta é uma pergunta mais antiga, mas mostra-se ativa recentemente. 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"

você pode usar opções para formatar a saída.

Nota:

Number.toLocaleString () arredonda após 16 casas decimais, para que ...

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

... retorna ...

586084736227728400000000000000000000000

Talvez isso seja indesejável se a precisão for importante no resultado pretendido.


2
Esta é a resposta infalível.
— Aria

Isso não funciona no PhantomJS 2.1.1 - ainda acaba em notação científica. Bem no Chrome.
— Egor Nepomnyaschih 01/03/19

1
Isso não parece funcionar para decimais muito pequenos: var myNumb = 0.0000000001; console.log (myNumb.toLocaleString ('fullwide', {useGrouping: false}));
— eh1160 26/08/19

Pode-se definir a opção maximumSignificantDigits (max 21) para formatar decimais muito pequenos, ou seja:js console.log( myNumb.toLocaleString('fullwide', { useGrouping: true, maximumSignificantDigits:6}) );
— Senhor. May

20

Para número pequeno e você sabe quantas casas decimais deseja, você pode usar toFixed e, em seguida, usar um regexp para remover os zeros à direita.

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

26
pergunta literallly pediu grande número
— Swyx

1
Tenha cuidado para evitar 0 como argumento da chamada toFixed - ela acabará apagando zeros à direita significativos:(1000).toFixed(0).replace(/\.?0+$/,"") // 1, not 1000
— Egor Nepomnyaschih

15

mais uma solução possível:

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

7
Isso não preserva o valor original ... por exemplo, 31415926535897932384626433832795 torna-se 31415926535897938480804068462624
— zero_cool

Não é positivo, mas provavelmente relacionado à maneira como o JavaScript lida com grandes números.
— zero_cool 13/01/19

@domsson Basicamente porque os números de ponto flutuante do IEEE são aritméticos. números em Javascript são realmente representados como número de ponto flutuante e "números decimais", não há "tipo exato inteiro" nativo. Você pode ler mais aqui: medium.com/dailyjs/javascripts-number-type-8d59199db1b6
— Pac0

@zero_cool Qualquer número maior do que Number.MAX_SAFE_INTEGERprovavelmente sofrerá esse problema.
— Pac0

10

Aqui está minha pequena variante do Number.prototype.toFixedmétodo que funciona com qualquer número:

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"


se você comparar Number.MAX_VALUE.toFixedSpecial (2) a Number.MAX_VALUE, verá que eles não são iguais.
— manonthemat

4
@manonthemat É claro que eles não são iguais, porque o primeiro é uma string formatada e o segundo é a Number. Se você converter o primeiro em a Number, verá que eles são absolutamente iguais: jsfiddle.net/qd6hpnyx/1 . Você pode retirar seu :P
— voto negativo

justo o suficiente ... Acabei de perceber que não posso, a menos que você esteja editando sua resposta.
— manonthemat

Melhor também substituir qualquer final .0por uma sequência vazia.
— vsync

Recebo um valor um tanto estranho quando tento console.log(2.2e307.toFixedSpecial(10)). Quero dizer ... eu recebo vários zeros à direita. Voto positivo de qualquer maneira, porque isso parece mais próximo do que eu preciso.
— Peter.petrov 08/10/19

6

A solução a seguir ignora a formatação exponencial automática para números muito grandes e muito pequenos. Esta é a solução da outis com uma correção de bug : não estava funcionando para números negativos muito pequenos.

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 Isso é o notório Javascript flutuante precisão de ponto problema, que é outra dor completamente diferente quando se utiliza números em JS ... Veja por exemplo stackoverflow.com/questions/1458633/...
— user56reinstatemonica8

3

As respostas de outras pessoas não fornecem o número exato!
Esta função calcula o número desejado com precisão e o retorna na string para evitar que seja alterado por javascript!
Se você precisar de um resultado numérico, basta multiplicar o resultado da função no número um!

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

Algumas informações adicionais sobre essa resposta apenas do código provavelmente seriam úteis.
— Pyves 19/07/19

Comentários adicionados à função.

2

Use .toPrecision, .toFixedetc. Você pode contar o número de dígitos no seu número convertendo-o em uma sequência e .toStringdepois olhando para ela .length.


por algum motivo, toPrecision não funciona. se você tentar: window.examplenum = 1352356324623461346, e depois dizer alert (window.examplenum.toPrecision (20)), ele não aparecer um alerta
— chris

na verdade, ele aparece algumas vezes mostrando notação científica e outras vezes não aparece. O que estou fazendo de errado?
— chris

18
Nenhum dos métodos de sugestão funciona para números grandes (ou pequenos). (2e64).toString()retornará "2e+64", então .lengthé inútil.
— CodeManX

2

Acabei usando isso para pegar o valor de uma entrada, expandindo números abaixo de 17 dígitos e convertendo números exponenciais para 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

Você pode fazer um loop sobre o número e obter o arredondamento

// funcionalidade para substituir char em determinado índice

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

// repetindo o número começa

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

Eu tentei trabalhar com a forma de string em vez do número e isso parecia funcionar. Eu só testei isso no Chrome, mas deve ser universal:

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

Eu acho que pode haver várias respostas semelhantes, mas aqui está uma coisa que eu inventei

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

Nota: isso é tão preciso quanto as funções matemáticas javascript e tem problemas ao usar log em vez de log10 na linha antes do loop for; ele escreverá 1000 na base 10 como 000, então eu mudei para log10 porque as pessoas geralmente usam a base 10 de qualquer maneira.

Esta pode não ser uma solução muito precisa, mas tenho orgulho em dizer que pode traduzir números com êxito em bases e vem com uma opção de vírgulas!


1

Sua pergunta:

number :0x68656c6c6f206f72656f
display:4.9299704811152646e+23

Você pode usar isso: https://github.com/MikeMcl/bignumber.js

Uma biblioteca JavaScript para aritmética decimal e não decimal de precisão arbitrária.

como isso:

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

bem, cuidado com o fato de que o bignumber.js não pode ir além de 15 dígitos significativos ...
— Michael Heuberger 29/03

0

Sei que muitos anos depois, mas eu estava trabalhando em um problema semelhante recentemente e queria postar minha solução. A resposta atualmente aceita preenche a parte expoente com zeros e a minha tenta encontrar a resposta exata, embora em geral não seja perfeitamente exata para números muito grandes devido ao limite de JS na precisão do ponto flutuante.

Isso funciona Math.pow(2, 100), retornando o valor correto de 1267650600228229401496703205376.

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 - sim, você está certo; Eu acho que tudo o que eu tentei anteriormente funcionou dessa maneira devido a outros fatores. Vou remover essa pequena nota na minha resposta.
— andi

1
não funciona:toFixed(Number.MAX_VALUE) == Number.MAX_VALUE
— manonthemat

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

com alguns problemas:

  • 0.9 é exibido como "0"
  • -0,9 é exibido como "-0"
  • 1e100 é exibido como "1"
  • funciona apenas para números até ~ 1e99 => use outra constante para números maiores; ou menor para otimização.

Sim, esses são alguns problemas.
— Adam Leggett

0

Estourando as expressões regulares. Isso não tem problemas de precisão e não é muito código.

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

Se você estiver apenas fazendo isso para exibição, poderá criar uma matriz a partir dos dígitos antes de serem arredondados.

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

Isso retornará a resposta correta, mas levará muito tempo e poderá terminar até o tempo limite da execução.
— 2127 Hannah

-1

Atualmente, não há função nativa para dissolver notação científica. No entanto, para esse fim, você deve escrever sua própria funcionalidade.

Aqui está o meu:

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("");
}

Isso falha para números realmente grandes. Eu tentei 2830869077153280552556547081187254342445169156730 e obtive 28308690771532805000000000000000000000000000000000000
— callback

-1

Você pode usar o módulo from-exponential . É leve e totalmente testado.

import fromExponential from 'from-exponential';

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

-1

Você também pode usar YourJS.fullNumber. Por exemplo, YourJS.fullNumber(Number.MAX_VALUE)resulta no seguinte: 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Também funciona para números realmente pequenos. YourJS.fullNumber(Number.MIN_VALUE)retorna isso: 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005

É importante notar que esta função sempre retornará números finitos como strings, mas retornará números não-finitos (por exemplo, NaNou Infinity) como undefined.

Você pode testá-lo no YourJS Console aqui .


-3

Você pode usar number.toString(10.1):

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

Nota: atualmente funciona no Chrome, mas não no Firefox. A especificação diz que o radical deve ser um número inteiro, portanto isso resulta em um comportamento não confiável.


o que diabos isso faz? Uma raiz de 10,1?
— joniba

Isso não funciona. Desde o ES5, as implementações são necessárias para usar o ToInteger no argumento. Então, usar 10.1 ou 10nada é equivalente.
— Oriol 12/01

Sim, isso realmente parou de funcionar um pouco recentemente. Meio triste, mas essa resposta agora é irrelevante.
— Zambonifofex 13/01

-6

Eu tive o mesmo problema com o oracle retornando notação científica, mas precisava do número real para um URL. Acabei de usar um truque PHP subtraindo zero e recebo o número correto.

por exemplo 5.4987E7 é o val.

newval = val - 0;

newval agora é igual a 54987000


2
Isso não tem efeito. 5.4987E7 tem um expoente menor que 21, portanto, ele aparece como o número completo, independentemente quando convertido em sequência. (5.4987E21 - 0) .toString () => "5.4987e + 21"
— Dwight
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.