Comment éviter la notation scientifique pour les grands nombres en JavaScript?


183

JavaScript convertit un grand INT en notation scientifique lorsque le nombre devient grand. Comment éviter que cela ne se produise?


1
Voulez-vous que 1E21 s'affiche sous la forme «1000000000000000000000»? Êtes-vous préoccupé par la façon dont le numéro est affiché ou comment il est stocké?
sortie

3
je suis préoccupé par la façon dont il est affiché: j'ai une commande document.write (myvariable)
chris

1
J'ai besoin du numéro dans le cadre d'une URL
chris

3
@outis: Les utilisateurs humains ne sont pas les seuls à vouloir lire les nombres. Il semble que D3 lèvera une exception lorsqu'il rencontrera une translatetransformation qui contient des coordonnées en notation scientifique.
OR Mapper

2
Il y a toujours une utilité pour cela lorsque vous traitez avec des nombres jusqu'à une certaine taille. La notation scientifique sera difficile à lire pour les utilisateurs qui ne connaissent pas trop la notation scientifique après tout.
hippietrail

Réponses:


110

Il y a Number.toFixed , mais il utilise la notation scientifique si le nombre est> = 1e21 et a une précision maximale de 20. À part cela, vous pouvez lancer le vôtre, mais ce sera compliqué.

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

Ci-dessus utilise la répétition de chaîne bon marché et facile ( (new Array(n+1)).join(str)). Vous pouvez définir l' String.prototype.repeatutilisation de la multiplication paysanne russe et l'utiliser à la place.

Cette réponse ne doit être appliquée qu'au contexte de la question: afficher un grand nombre sans utiliser de notation scientifique. Pour toute autre chose, vous devez utiliser une bibliothèque BigInt , telle que BigNumber , Leemon's BigInt ou BigInteger . À l'avenir, le nouveau BigInt natif (note: pas celui de Leemon) devrait être disponible; Chrome et les navigateurs basés sur celui-ci ( Chrome , le nouveau Edge [v79 +], Brave ) et Firefox ont tous un support; Le support de Safari est en cours.

Voici comment vous utiliseriez BigInt pour cela: BigInt(n).toString()

Exemple:

Attention , cependant, que tout entier vous sortie en tant que numéro JavaScript (pas BigInt) qui est plus de 15-16 chiffres ( en particulier, supérieur à Number.MAX_SAFE_INTEGER + 1[9.007.199.254.740.992]) peut être être arrondi, car le type de numéro JavaScript (double précision IEEE-754 virgule flottante) ne peut pas contenir précisément tous les nombres entiers au-delà de ce point. À partir du moment où Number.MAX_SAFE_INTEGER + 1il fonctionne par multiples de 2, il ne peut donc plus contenir de nombres impairs (et de manière similaire, à 18 014 398 509 481 984, il commence à fonctionner par multiples de 4, puis 8, puis 16, ...).

Par conséquent, si vous pouvez compter sur le BigIntsupport, affichez votre numéro sous forme de chaîne que vous passez à la BigIntfonction:

const n = BigInt("YourNumberHere");

Exemple:


Votre solution me donne un résultat très différent pour 2 ^ 1000 que wolframalpha. Des pointeurs?
Shane Reustle

4
@Shane: Cette Q&R concerne l'affichage des nombres à virgule flottante sous forme d'entiers en base 10 et n'adresse pas les nombres qui ne peuvent pas être représentés dans un format à virgule flottante (qui se produira lors de la conversion en base 10). Vous avez besoin d'une bibliothèque JS bigint , comme indiqué dans la dernière ligne.
sortie

1
@PeterOlson: on dirait que j'ai oublié un Math.abs. Merci pour l'information.
sortie

1
toFixed(Number.MAX_VALUE) == Number.MAX_VALUEdevrait retourner vrai alors, mais ce n'est pas le cas ...
manonthemat

3
En fait, ce code tel qu'il est ne fonctionne pas pour de très petits nombres négatifs: toFixed (-1E-20) -> "0.0000000000000000000.09999999999999999"
davidthings

44

Je sais que c'est une question plus ancienne, mais montre récemment active. 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"

vous pouvez utiliser des options pour formater la sortie.

Remarque:

Number.toLocaleString () arrondit après 16 décimales, de sorte que ...

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

...Retour...

586084736227728400000000000000000000000

Ceci n'est peut-être pas souhaitable si la précision est importante dans le résultat attendu.


2
C'est la réponse infaillible.
Aria

Cela ne fonctionne pas dans PhantomJS 2.1.1 - cela se termine toujours en notation scientifique. Très bien en Chrome.
Egor Nepomnyaschih

1
Cela ne semble pas fonctionner pour de très petites décimales: var myNumb = 0.0000000001; console.log (myNumb.toLocaleString ('fullwide', {useGrouping: false}));
eh1160

On peut définir l'option maximumSignificantDigits (max 21) pour formater de très petites décimales, c'est-à-dire:js console.log( myNumb.toLocaleString('fullwide', { useGrouping: true, maximumSignificantDigits:6}) );
Monsieur. MaNiAl

20

Pour un petit nombre, et vous savez combien de décimales vous voulez, vous pouvez utiliser toFixed puis utiliser une expression régulière pour supprimer les zéros de fin.

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

26
question littéralement posée pour un grand nombre
swyx

1
Attention à éviter 0 comme argument de l'appel toFixed - cela finira par effacer les zéros de fin significatifs:(1000).toFixed(0).replace(/\.?0+$/,"") // 1, not 1000
Egor Nepomnyaschih

15

une autre solution possible:

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

7
Cela ne préserve pas la valeur d'origine ... par exemple 31415926535897932384626433832795 devient 31415926535897938480804068462624
zero_cool

Pas positif, mais probablement lié à la façon dont JavaScript gère les grands nombres.
zero_cool

@domsson Fondamentalement, parce que l'arithmétique des nombres à virgule flottante IEEE s'applique. les nombres en Javascript sont en fait représentés comme des nombres à virgule flottante ainsi que des "nombres décimaux", il n'y a pas de "type exact entier" natif. Vous pouvez en savoir plus ici: medium.com/dailyjs/javascripts-number-type-8d59199db1b6
Pac0

@zero_cool Tout nombre supérieur à celui qui Number.MAX_SAFE_INTEGERest susceptible de souffrir de ce problème.
Pac0

10

Voici ma courte variante de Number.prototype.toFixedméthode qui fonctionne avec n'importe quel nombre:

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"


si vous comparez Number.MAX_VALUE.toFixedSpecial (2) à Number.MAX_VALUE, vous verrez qu'ils ne sont pas égaux.
manonthemat

4
@manonthemat Bien sûr, ils ne sont pas égaux, car le premier est une chaîne formatée et le second est un Number. Si vous lancez le premier en a Number, vous verrez qu'ils sont absolument égaux: jsfiddle.net/qd6hpnyx/1 . Vous pouvez retirer votre :P
vote négatif

assez juste ... J'ai juste remarqué que je ne peux pas, à moins que vous ne modifiiez votre réponse.
manonthemat

Mieux vaut également remplacer toute fin .0par une chaîne vide.
vsync le

J'obtiens une valeur un peu étrange lorsque j'essaye console.log(2.2e307.toFixedSpecial(10)). Je veux dire ... j'obtiens plusieurs zéros à la fin. De toute façon, voter pour parce que cela me semble le plus proche de ce dont j'ai besoin.
peter.petrov

6

La solution suivante contourne le formatage exponentiel automatique pour les très grands et très petits nombres. C'est la solution d' Outis avec un bug: cela ne fonctionnait pas pour de très petits nombres négatifs.

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 C'est le problème notoire de précision en virgule flottante Javascript , qui est une autre douleur complètement différente lors de l'utilisation de nombres dans JS ... Voir par exemple stackoverflow.com/questions/1458633/…
user56reinstatemonica8

3

Les réponses des autres ne vous donnent pas le nombre exact!
Cette fonction calcule le nombre souhaité avec précision et le renvoie dans la chaîne pour éviter qu'il ne soit modifié par javascript!
Si vous avez besoin d'un résultat numérique, multipliez simplement le résultat de la fonction par le numéro un!

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

Des informations supplémentaires sur cette réponse code seulement seraient probablement utiles.
Pyves

Commentaires ajoutés à la fonction.

2

Utilisation .toPrecision, .toFixedetc. Vous pouvez compter le nombre de chiffres dans votre numéro en le convertissant en une chaîne avec la .toStringrecherche puis à son .length.


pour une raison quelconque, toPrecision ne fonctionne pas. si vous essayez: window.examplenum = 1352356324623461346, puis dites alert (window.examplenum.toPrecision (20)), il n'apparaît pas d'alerte
chris

en fait, il apparaît parfois avec une notation scientifique, et d'autres fois, il n'apparaît pas du tout. Qu'est-ce que je fais mal?
chris

18
Aucune des méthodes de suggestion ne fonctionne pour les grands (ou petits) nombres. (2e64).toString()reviendra "2e+64", donc .lengthest inutile.
CodeManX

2

C'est ce que j'ai fini par utiliser pour prendre la valeur d'une entrée, en développant les nombres de moins de 17 chiffres et en convertissant les nombres exponentiels en 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

Vous pouvez boucler le nombre et réaliser l'arrondi

// fonctionnalité pour remplacer char à un index donné

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

// boucle sur le nombre commence

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

J'ai essayé de travailler avec la forme de chaîne plutôt que le nombre et cela semblait fonctionner. Je n'ai testé cela que sur Chrome mais cela devrait être universel:

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

Je pense qu'il peut y avoir plusieurs réponses similaires, mais voici une chose que j'ai trouvée

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

Remarque: cela n'est aussi précis que les fonctions mathématiques javascript et pose des problèmes lors de l'utilisation de log au lieu de log10 sur la ligne avant la boucle for; il écrira 1000 en base-10 comme 000 donc je l'ai changé en log10 parce que les gens utiliseront principalement la base-10 de toute façon.

Ce n'est peut-être pas une solution très précise, mais je suis fier de dire qu'elle peut traduire avec succès des nombres d'une base à l'autre et est livrée avec une option pour les virgules!


1

Ta question:

number :0x68656c6c6f206f72656f
display:4.9299704811152646e+23

Vous pouvez utiliser ceci: https://github.com/MikeMcl/bignumber.js

Une bibliothèque JavaScript pour l'arithmétique décimale et non décimale de précision arbitraire.

comme ça:

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

eh bien, méfiez-vous que bignumber.js ne peut pas dépasser 15 chiffres significatifs ...
Michael Heuberger

0

Je sais que c'est plusieurs années plus tard, mais j'avais récemment travaillé sur un problème similaire et je voulais publier ma solution. La réponse actuellement acceptée remplit la partie exposant avec des 0, et la mienne tente de trouver la réponse exacte, bien qu'en général elle ne soit pas parfaitement précise pour les très grands nombres en raison de la limite de JS en précision en virgule flottante.

Cela fonctionne pour Math.pow(2, 100), renvoyant la valeur correcte 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 - ouais, vous avez raison; Je suppose que tout ce que j'ai essayé avec précédemment fonctionnait de cette façon en raison d'autres facteurs. Je vais supprimer cette petite note dans ma réponse.
andi

1
ne fonctionne pas:toFixed(Number.MAX_VALUE) == Number.MAX_VALUE
manonthemat

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

avec quelques problèmes:

  • 0.9 est affiché comme "0"
  • -0,9 s'affiche sous la forme "-0"
  • 1e100 est affiché comme "1"
  • ne fonctionne que pour les nombres jusqu'à ~ 1e99 => utilisez une autre constante pour les nombres plus grands; ou plus petit pour l'optimisation.

Ouais, ce sont des problèmes.
Adam Leggett

0

Briser les expressions régulières. Cela n'a pas de problèmes de précision et ce n'est pas beaucoup de code.

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

Si vous le faites uniquement pour l'affichage, vous pouvez créer un tableau à partir des chiffres avant qu'ils ne soient arrondis.

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

Cela renverra la bonne réponse, mais prendra beaucoup de temps et pourrait se terminer pour que l'exécution expire.
Hannah mai

-1

Actuellement, il n'y a pas de fonction native pour dissoudre la notation scientifique. Cependant, pour cela, vous devez écrire votre propre fonctionnalité.

Voici mon:

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

Cela échoue pour de très gros nombres. J'ai essayé 2830869077153280552556547081187254342445169156730 et j'ai obtenu 2830869077153280500000000000000000000000000000000
rappel

-1

Vous pouvez utiliser le module exponentiel . Il est léger et entièrement testé.

import fromExponential from 'from-exponential';

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

-1

Vous pouvez également utiliser YourJS.fullNumber. Par exemple, cela YourJS.fullNumber(Number.MAX_VALUE)donne les résultats suivants: 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Cela fonctionne également pour de très petits nombres. YourJS.fullNumber(Number.MIN_VALUE)renvoie ceci: 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005

Il est important de noter que cette fonction retournera toujours des nombres finis sous forme de chaînes mais renverra des nombres non finis (par exemple NaNou Infinity) sous forme de undefined.

Vous pouvez le tester dans la console YourJS ici .


-3

Vous pouvez utiliser number.toString(10.1):

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

Remarque: cela fonctionne actuellement dans Chrome, mais pas dans Firefox. La spécification indique que la base doit être un entier, ce qui entraîne un comportement peu fiable.


qu'est-ce que ça fait? Une base de 10,1?
joniba

Cela ne marche pas. Depuis ES5, les implémentations doivent utiliser ToInteger sur l'argument. Donc utiliser 10.1 ou 10ou rien est équivalent.
Oriol

Oui, cela a en effet cessé de fonctionner un peu récemment. Un peu triste, mais cette réponse est désormais hors de propos.
Zambonifofex

-6

J'ai eu le même problème avec oracle retournant la notation scientifique, mais j'avais besoin du numéro réel pour une URL. Je viens d'utiliser une astuce PHP en soustrayant zéro, et j'obtiens le bon nombre.

par exemple 5.4987E7 est le val.

newval = val - 0;

newval vaut maintenant 54987000


2
Cela n'a aucun effet. 5.4987E7 a un exposant inférieur à 21, donc il apparaît comme le nombre complet indépendamment de sa conversion en chaîne. (5.4987E21 - 0) .toString () => "5.4987e + 21"
Dwight
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.