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?

chris
fonte
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

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:

fora
fonte
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.

flapjack17
fonte
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
Niklas Forsström
fonte
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;
}
Mirodil
fonte
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"

Visão
fonte
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));

Jenny O'Reilly
fonte
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;
}

fonte
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.


fonte
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;
  }
}
Tyeth
fonte
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--;
 }
}
Kapil gopinath
fonte
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;
}
Sanford Staab
fonte
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!

cmarangu
fonte
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
super2bai
fonte
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

e eu
fonte
@ 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.
Wiimm
fonte
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));

Adam Leggett
fonte
-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(''));
Jake M
fonte
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("");
}
Martin Wantke
fonte
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'
shrpne
fonte
-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 .

Chris West
fonte
-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.

Zambonifofex
fonte
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

RaulQ
fonte
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