Come evitare la notazione scientifica per grandi numeri in JavaScript?


183

JavaScript converte un grande INT in notazione scientifica quando il numero diventa grande. Come posso evitare che ciò accada?


1
Desideri che 1E21 venga visualizzato come '1000000000000000000000'? Ti preoccupi di come viene visualizzato il numero o di come viene memorizzato?
uscirà il

3
sono preoccupato per come viene visualizzato: ho un comando document.write (myvariable)
chris

1
ho bisogno del numero come parte di un URL
chris,

3
@outis: gli utenti umani non sono gli unici a voler leggere i numeri. Sembra che D3 genererà un'eccezione quando incontrerà una translatetrasformazione che contiene coordinate in notazione scientifica.
OR Mapper

2
È ancora utile per questo quando hai a che fare con numeri fino a una certa dimensione. La notazione scientifica sarà difficile da leggere per gli utenti che non conoscono troppo la notazione scientifica.
hippietrail,

Risposte:


110

C'è Number.toFixed , ma usa la notazione scientifica se il numero è> = 1e21 e ha una precisione massima di 20. A parte questo, puoi lanciare il tuo, ma sarà disordinato.

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

Sopra usa la ripetizione di stringhe cheap-'n'-easy ( (new Array(n+1)).join(str)). Puoi definire String.prototype.repeatusando la moltiplicazione contadina russa e usarla invece.

Questa risposta dovrebbe essere applicata solo al contesto della domanda: visualizzare un numero elevato senza usare la notazione scientifica. Per ogni altra cosa, dovresti usare una libreria BigInt , come BigNumber , Leemon's BigInt o BigInteger . In futuro, dovrebbe essere disponibile il nuovo BigInt nativo (nota: non di Leemon); Chromium e browser basati su di esso ( Chrome , il nuovo Edge [v79 +], Brave ) e Firefox hanno tutti supporto; Il supporto di Safari è in corso.

Ecco come useresti BigInt per questo: BigInt(n).toString()

Esempio:

Fai attenzione , tuttavia, che qualsiasi numero intero che hai emesso come numero JavaScript (non come BigInt) che è più di 15-16 cifre (in particolare, maggiore di Number.MAX_SAFE_INTEGER + 1[9.007.199.254.740.992]) può essere arrotondato, perché il tipo di numero JavaScript (IEEE-754 doppia precisione virgola mobile) non può contenere con precisione tutti i numeri interi oltre quel punto. Al Number.MAX_SAFE_INTEGER + 1momento funziona in multipli di 2, quindi non può più contenere numeri dispari (e allo stesso modo, a 18.014.398.509.481.984 inizia a lavorare in multipli di 4, quindi 8, quindi 16, ...).

Di conseguenza, se puoi fare affidamento sul BigIntsupporto, emetti il ​​tuo numero come una stringa che passi alla BigIntfunzione:

const n = BigInt("YourNumberHere");

Esempio:


La tua soluzione mi dà un risultato molto diverso per 2 ^ 1000 rispetto a wolframalpha. Qualche puntatore?
Shane Reustle,

4
@Shane: questo Q&A riguarda la visualizzazione di numeri in virgola mobile come numeri interi in base 10 e non affronta i numeri che non possono essere rappresentati in un formato in virgola mobile (che sorgerà quando si converte in base 10). Hai bisogno di una libreria JS bigint , come menzionato nella riga finale.
uscita l'

1
@PeterOlson: sembra che ho lasciato fuori a Math.abs. Grazie per il testa a testa.
uscita

1
toFixed(Number.MAX_VALUE) == Number.MAX_VALUEdovrebbe tornare vero allora, ma non ...
manonthemat

3
In realtà questo codice così com'è non funziona per numeri negativi molto piccoli: toFixed (-1E-20) -> "0.0000000000000000000.0999999999999999999"
davidthings,

44

So che questa è una domanda più vecchia, ma mostra di recente attiva. 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"

è possibile utilizzare le opzioni per formattare l'output.

Nota:

Number.toLocaleString () arrotonda dopo 16 cifre decimali, in modo che ...

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

...ritorna...

586084736227728400000000000000000000000

Ciò è forse indesiderabile se l'accuratezza è importante nel risultato previsto.


2
Questa è la risposta infallibile.
Aria,

Questo non funziona in PhantomJS 2.1.1 - finisce ancora in notazione scientifica. Bene in Chrome.
Egor Nepomnyaschih

1
Questo non sembra funzionare con decimali molto piccoli: var myNumb = 0.0000000001; console.log (myNumb.toLocaleString ('fullwide', {useGrouping: false}));
eh1160,

È possibile impostare l'opzione maximumSignificantDigits (max 21) per formattare decimali molto piccoli, ovvero:js console.log( myNumb.toLocaleString('fullwide', { useGrouping: true, maximumSignificantDigits:6}) );
Signore. MaNiAl

20

Per un piccolo numero e sai quanti decimali vuoi, puoi usare Fisso e quindi usare una regexp per rimuovere gli zeri finali.

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

26
domanda letteralmente posta per gran numero
swyx

1
Fai attenzione a evitare 0 come argomento della chiamata fissa - finirà per cancellare zeri finali significativi:(1000).toFixed(0).replace(/\.?0+$/,"") // 1, not 1000
Egor Nepomnyaschih

15

un'altra soluzione possibile:

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

7
Questo non conserva il valore originale ... ad esempio 31415926535897932384626433832795 diventa 31415926535897938480804068462624
zero_cool

Non è positivo, ma probabilmente ha a che fare con il modo in cui JavaScript gestisce numeri di grandi dimensioni.
zero_fresco

@domsson Fondamentalmente perché si applica l'aritmetica dei numeri in virgola mobile IEEE. i numeri in Javascript sono in realtà rappresentati come numeri in virgola mobile e "numeri decimali", non esiste un "tipo esatto intero" nativo. Puoi leggere di più qui: medium.com/dailyjs/javascripts-number-type-8d59199db1b6
Pac0

@zero_cool Qualsiasi numero maggiore di Number.MAX_SAFE_INTEGERè probabile che subisca questo problema.
Pac0,

10

Ecco la mia breve variante del Number.prototype.toFixedmetodo che funziona con qualsiasi numero:

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 confronti Number.MAX_VALUE.toFixedSpecial (2) con Number.MAX_VALUE, vedrai che non sono uguali.
manonthemat,

4
@manonthemat Naturalmente non sono uguali, perché la prima è una stringa formattata e la seconda è a Number. Se lanci il primo in a Number, vedrai che sono assolutamente uguali: jsfiddle.net/qd6hpnyx/1 . Puoi riprendere il tuo :P
voto negativo

abbastanza giusto .... Ho appena notato che non posso, a meno che tu non stia modificando la tua risposta.
manonthemat,

Meglio sostituire anche qualsiasi trailing .0con stringa vuota.
vsync,

Ottengo un valore alquanto strano quando provo console.log(2.2e307.toFixedSpecial(10)). Voglio dire ... ho diversi zeri finali. Voto comunque perché questo sembra più vicino a ciò di cui ho bisogno.
peter.petrov,

6

La seguente soluzione ignora la formattazione esponenziale automatica per numeri molto grandi e molto piccoli. Questa è la soluzione di Outis con un bugfix: non funzionava con numeri negativi molto piccoli.

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 Questo è il famigerato Javascript precisione in virgola mobile problema, che è un altro dolore completamente diversa quando si usano i numeri in JS ... vedi ad esempio stackoverflow.com/questions/1458633/...
user56reinstatemonica8

3

Le risposte degli altri non ti danno il numero esatto!
Questa funzione calcola accuratamente il numero desiderato e lo restituisce nella stringa per impedirne la modifica tramite JavaScript!
Se hai bisogno di un risultato numerico, basta moltiplicare il risultato della funzione nel numero uno!

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

Alcune informazioni aggiuntive su questa unica risposta al codice sarebbero probabilmente utili.
Pyves,

Commenti aggiunti alla funzione.

2

Usa .toPrecision, .toFixedecc. Puoi contare il numero di cifre nel tuo numero convertendolo in una stringa e .toStringosservandolo .length.


per qualche motivo, toPrecision non funziona. se provi: window.examplenum = 1352356324623461346, quindi pronuncia alert (window.examplenum.toPrecision (20)), non viene visualizzato un avviso
chris

in realtà si apre a volte mostrando notazione scientifica, e altre volte non si presenta affatto. Che cosa sto facendo di sbagliato?
chris,

18
Nessuno dei due metodi di suggerimento funziona per numeri grandi (o piccoli). (2e64).toString()tornerà "2e+64", quindi .lengthè inutile.
CodeManX,

2

Questo è quello che ho finito per prendere il valore da un input, espandendo i numeri a meno di 17 cifre e convertendo i numeri esponenziali in 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

Puoi scorrere il numero e ottenere l'arrotondamento

// funzionalità per sostituire il carattere in un determinato indice

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

// inizia il ciclo sul numero

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

Ho provato a lavorare con il modulo stringa anziché con il numero e questo sembra funzionare. L'ho provato solo su Chrome ma dovrebbe essere universale:

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

Penso che ci possano essere diverse risposte simili, ma ecco una cosa che mi è venuta in mente

// 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: questo è accurato solo come le funzioni matematiche javascript e ha problemi quando si utilizza log anziché log10 sulla riga prima del ciclo for; scriverà 1000 in base-10 come 000, quindi l'ho cambiato in log10 perché le persone useranno principalmente la base-10 comunque.

Questa potrebbe non essere una soluzione molto accurata, ma sono orgoglioso di dire che può tradurre con successo i numeri attraverso le basi e viene fornito con un'opzione per le virgole!


1

La tua domanda:

number :0x68656c6c6f206f72656f
display:4.9299704811152646e+23

Puoi usare questo: https://github.com/MikeMcl/bignumber.js

Una libreria JavaScript per l'aritmetica decimale e non decimale di precisione arbitraria.

come questo:

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

beh, attenzione che bignumber.js non può andare oltre 15 cifre significative ...
Michael Heuberger,

0

So che sono passati molti anni, ma di recente avevo lavorato su un problema simile e volevo pubblicare la mia soluzione. La risposta attualmente accettata completa la parte esponente con 0 e la mia tenta di trovare la risposta esatta, sebbene in generale non sia perfettamente accurata per numeri molto grandi a causa del limite di JS nella precisione in virgola mobile.

Questo funziona per Math.pow(2, 100), restituendo il valore corretto di 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 - sì, hai ragione; Immagino che qualunque cosa io abbia provato in precedenza ha funzionato in quel modo a causa di altri fattori. Rimuoverò quella piccola nota nella mia risposta.
andi

1
non funziona:toFixed(Number.MAX_VALUE) == Number.MAX_VALUE
manonthemat

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

con alcuni problemi:

  • 0.9 viene visualizzato come "0"
  • -0.9 viene visualizzato come "-0"
  • 1e100 viene visualizzato come "1"
  • funziona solo per numeri fino a ~ 1e99 => usa altra costante per numeri maggiori; o inferiore per l'ottimizzazione.

Sì, quelli sono alcuni problemi.
Adam Leggett

0

Eliminare le espressioni regolari. Questo non ha problemi di precisione e non è un sacco di codice.

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 lo stai semplicemente facendo per la visualizzazione, puoi costruire un array dalle cifre prima che siano arrotondate.

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

Ciò restituirà la risposta giusta, ma richiederà così tanto tempo e potrebbe finire per il timeout dell'esecuzione.
Hannah,

-1

Attualmente non esiste una funzione nativa per dissolvere la notazione scientifica. Tuttavia, a tal fine è necessario scrivere le proprie funzionalità.

Ecco il mio:

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

Questo fallisce per numeri davvero grandi. Ho provato 2830869077153280552556547081187254342445169156730 e ho ottenuto 28308690771532805000000000000000000000000000000000000000000000000
richiamata

-1

È possibile utilizzare il modulo from-esponential . È leggero e completamente testato.

import fromExponential from 'from-exponential';

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

-1

Puoi anche usare YourJS.fullNumber. Ad esempio, si YourJS.fullNumber(Number.MAX_VALUE)ottiene quanto segue: 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Funziona anche con numeri molto piccoli. YourJS.fullNumber(Number.MIN_VALUE)restituisce questo: 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005

È importante notare che questa funzione restituirà sempre numeri finiti come stringhe ma restituirà numeri non finiti (ad es. NaNOppure Infinity) come undefined.

Puoi provarlo nella tua console YourJS qui .


-3

Puoi usare number.toString(10.1):

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

Nota: attualmente funziona in Chrome, ma non in Firefox. La specifica dice che il radix deve essere un numero intero, quindi questo comporta un comportamento inaffidabile.


cosa diavolo fa questo? Una radix di 10.1?
Joniba,

Questo non funziona Da ES5, le implementazioni sono necessarie per utilizzare ToInteger sull'argomento. Quindi usare 10.1 o 10o niente sono equivalenti.
Oriol,

Sì, questo ha effettivamente smesso di funzionare un po 'di recente. Un po 'triste, ma questa risposta è ora irrilevante.
Zambonifofex,

-6

Ho avuto lo stesso problema con l'oracolo che restituiva la notazione scientifica, ma avevo bisogno del numero effettivo per un url. Ho appena usato un trucco PHP sottraendo zero e ottengo il numero corretto.

per esempio 5.4987E7 è il val.

newval = val - 0;

newval ora equivale a 54987000


2
Questo non ha alcun effetto. 5.4987E7 ha un esponente inferiore a 21, quindi viene visualizzato come numero intero indipendentemente dalla conversione in stringa. (5.4987E21 - 0) .toString () => "5.4987e + 21"
Dwight
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.