¿Cómo evitar la notación científica para grandes números en JavaScript?

183

JavaScript convierte un gran INT a notación científica cuando el número se vuelve grande. ¿Cómo puedo evitar que esto suceda?

Chris
fuente
1
¿Desea que 1E21 se muestre como '1000000000000000000000'? ¿Le preocupa cómo se muestra el número o cómo se almacena?
outis
3
Estoy preocupado por la forma en que se muestra: Tengo un comando document.write (myvariable)
Chris
1
Necesito el número como parte de una URL
Chris
3
@outis: Los usuarios humanos no son los únicos que quieren leer números. Parece que D3 arrojará una excepción cuando encuentre una translatetransformación que contenga coordenadas en notación científica.
O Mapper
2
Todavía hay un uso para esto cuando se trata de números de hasta cierto tamaño. La notación científica será difícil de leer para los usuarios que no conocen la notación científica también después de todo.
hippietrail

Respuestas:

110

Hay Number.toFixed , pero usa notación científica si el número es> = 1e21 y tiene una precisión máxima de 20. Aparte de eso, puedes rodar el tuyo, pero será complicado.

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

El anterior utiliza la repetición de cadenas cheap-'n'-easy ( (new Array(n+1)).join(str)). Puede definir el String.prototype.repeatuso de la multiplicación campesina rusa y utilizarlo en su lugar.

Esta respuesta solo debe aplicarse al contexto de la pregunta: mostrar un gran número sin usar notación científica. Para cualquier otra cosa, debe usar una biblioteca BigInt , como BigNumber , Leemon's BigInt o BigInteger . En el futuro, el nuevo BigInt nativo (nota: no Leemon's) debería estar disponible; Chromium y los navegadores basados ​​en él ( Chrome , el nuevo Edge [v79 +], Brave ) y Firefox tienen soporte; El apoyo de Safari está en marcha.

Así es como usarías BigInt para ello: BigInt(n).toString()

Ejemplo:

Sin embargo, tenga en cuenta que cualquier número entero que ingrese como un número de JavaScript (no un BigInt) que tenga más de 15-16 dígitos (específicamente, mayor que Number.MAX_SAFE_INTEGER + 1[9,007,199,254,740,992]) puede redondearse, porque el tipo de número de JavaScript (IEEE-754 de doble precisión punto flotante) no puede contener con precisión todos los enteros más allá de ese punto. A partir de Number.MAX_SAFE_INTEGER + 1que funciona en múltiplos de 2, por lo que ya no puede contener números impares (y de manera similar, a 18,014,398,509,481,984 comienza a funcionar en múltiplos de 4, luego 8, luego 16, ...).

En consecuencia, si puede confiar en el BigIntsoporte, envíe su número como una cadena que pase a la BigIntfunción:

const n = BigInt("YourNumberHere");

Ejemplo:

outis
fuente
Su solución me da un resultado muy diferente para 2 ^ 1000 que wolframalpha. Cualquier puntero?
Shane Reustle
44
@Shane: Este Q&A se trata de mostrar números de punto flotante como enteros de base 10 y no trata los números que no se pueden representar en un formato de punto flotante (que surgirá al convertir a base 10). Necesita una biblioteca JS bigint , como se menciona en la línea final.
outis
1
@PeterOlson: parece que dejé de lado a Math.abs. Gracias por el aviso.
outis
1
toFixed(Number.MAX_VALUE) == Number.MAX_VALUEdebe devolver cierto entonces, pero no es así ...
manonthemat
3
En realidad, este código tal como está no funciona para números negativos muy pequeños: toFixed (-1E-20) -> "0.0000000000000000000.09999999999999999"
davidthings
44

Sé que esta es una pregunta anterior, pero se muestra recientemente activa. 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"

puede usar opciones para formatear la salida.

Nota:

Number.toLocaleString () se redondea después de 16 decimales, de modo que ...

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

...devoluciones...

586084736227728400000000000000000000000

Esto quizás no sea deseable si la precisión es importante en el resultado deseado.

flapjack17
fuente
2
Esta es la respuesta infalible.
Aria
Esto no funciona en PhantomJS 2.1.1, todavía termina en notación científica. Bien en cromo.
Egor Nepomnyaschih
1
Esto no parece funcionar para decimales muy pequeños: var myNumb = 0.0000000001; console.log (myNumb.toLocaleString ('fullwide', {useGrouping: false}));
eh1160
Se puede configurar la opción maximumSignificantDigits (max 21) para formatear decimales muy pequeños, es decir:js console.log( myNumb.toLocaleString('fullwide', { useGrouping: true, maximumSignificantDigits:6}) );
Sir. Manial
20

Para un número pequeño, y sabe cuántos decimales desea, puede usar toFixed y luego usar una expresión regular para eliminar los ceros finales.

Number(1e-7).toFixed(8).replace(/\.?0+$/,"") //0.000
Niklas Forsström
fuente
26
pregunta literalmente
hecha
1
Tenga cuidado de evitar 0 como argumento de la llamada toFixed: terminará borrando ceros a la izquierda significativos:(1000).toFixed(0).replace(/\.?0+$/,"") // 1, not 1000
Egor Nepomnyaschih
15

Una posible solución más:

function toFix(i){
 var str='';
 do{
   let a = i%10;
   i=Math.trunc(i/10);
   str = a+str;
 }while(i>0)
 return str;
}
Mirodil
fuente
77
Esto no conserva el valor original ... por ejemplo 31415926535897932384626433832795 se convierte en 31415926535897938480804068462624
zero_cool
No es positivo, pero es probable que tenga que ver con cómo JavaScript maneja grandes números.
zero_cool
@domsson Básicamente porque se aplican los números de coma flotante IEEE. los números en Javascript se representan realmente como números de coma flotante, así como "números decimales", no hay un "tipo exacto entero" nativo. Puede leer más aquí: medium.com/dailyjs/javascripts-number-type-8d59199db1b6
Pac0
@zero_cool Cualquier número mayor que Number.MAX_SAFE_INTEGERes probable que sufra este problema.
Pac0
10

Aquí está mi pequeña variante de Number.prototype.toFixedmétodo que funciona con cualquier 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"

Visión
fuente
si compara Number.MAX_VALUE.toFixedSpecial (2) con Number.MAX_VALUE, verá que no son iguales.
manonthemat
44
@manonthemat Por supuesto, no son iguales, porque el primero es una cadena formateada y el segundo es un Number. Si lanza el primero a un Number, verá que son absolutamente iguales: jsfiddle.net/qd6hpnyx/1 . Puede retirar su :P
voto negativo
es justo ... acabo de notar que no puedo, a menos que estés editando tu respuesta.
manonthemat
Es mejor reemplazar también cualquier final .0con una cadena vacía.
vsync
Obtengo un valor un tanto extraño cuando lo intento console.log(2.2e307.toFixedSpecial(10)). Quiero decir ... obtengo varios ceros al final. Votar de todos modos porque esto parece más cercano a lo que necesito.
peter.petrov
6

La siguiente solución omite el formato exponencial automático para números muy grandes y muy pequeños. Esta es la solución de outis con una corrección de errores : no funcionaba para números negativos muy pequeños.

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
fuente
1
numberToString (0.0000002) "0.00000019999999999999998"
raphadko
1
@raphadko Ese es el notorio problema de precisión de coma flotante de Javascript , que es otro dolor completamente diferente cuando se usan números en JS ... Ver por ejemplo stackoverflow.com/questions/1458633/…
user56reinstatemonica8
3

¡Las respuestas de los demás no te dan el número exacto!
¡Esta función calcula el número deseado con precisión y lo devuelve en la cadena para evitar que JavaScript lo cambie!
Si necesita un resultado numérico, ¡simplemente multiplique el resultado de la función en el número 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;
}

fuente
Alguna información adicional sobre este código solo responde probablemente sería útil.
Pyves
Comentarios agregados a la función.
2

Uso .toPrecision, .toFixedetc. Puede contar el número de dígitos en su número al convertirlo en una cadena y .toStringluego mirarlo .length.


fuente
por alguna razón, toPrecision no funciona. si intentas: window.examplenum = 1352356324623461346, y luego dices alert (window.examplenum.toPrecision (20)), no aparece una alerta
Chris
en realidad aparece a veces mostrando notación científica, y otras veces no aparece en absoluto. ¿Qué estoy haciendo mal?
Chris
18
Ninguno de los métodos de sugerencia funciona para números grandes (o pequeños). (2e64).toString()volverá "2e+64", por lo que .lengthes inútil.
CodeManX
2

Esto es lo que terminé usando para tomar el valor de una entrada, expandiendo números de menos de 17 dígitos y convirtiendo números exponenciales a 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
fuente
1

Puede recorrer el número y lograr el redondeo

// funcionalidad para reemplazar char en un índice dado

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

// comienza el bucle sobre el número

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
fuente
1

Traté de trabajar con la forma de cadena en lugar del número y esto pareció funcionar. Solo he probado esto en Chrome, pero debería 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
fuente
1

Creo que puede haber varias respuestas similares, pero aquí hay algo que se me ocurrió

// 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: esto es tan preciso como las funciones matemáticas de JavaScript y tiene problemas al usar log en lugar de log10 en la línea anterior al ciclo for; escribirá 1000 en base-10 como 000, así que lo cambié a log10 porque la gente usará la base-10 de todos modos.

Puede que esta no sea una solución muy precisa, pero me enorgullece decir que puede traducir con éxito números a través de bases y que viene con una opción para comas.

cmarangu
fuente
1

Tu pregunta:

number :0x68656c6c6f206f72656f
display:4.9299704811152646e+23

Puede usar esto: https://github.com/MikeMcl/bignumber.js

Una biblioteca de JavaScript para aritmética decimal y no decimal de precisión arbitraria.

Me gusta esto:

let ten =new BigNumber('0x68656c6c6f206f72656f',16);
console.log(ten.toString(10));
display:492997048111526447310191
super2bai
fuente
bueno, cuidado con que bignumber.js no puede ir más allá de 15 dígitos significativos ...
Michael Heuberger
0

Sé que han pasado muchos años, pero recientemente había estado trabajando en un problema similar y quería publicar mi solución. La respuesta actualmente aceptada rellena la parte del exponente con 0, y la mía intenta encontrar la respuesta exacta, aunque en general no es perfectamente precisa para números muy grandes debido al límite de JS en la precisión de coma flotante.

Esto funciona para Math.pow(2, 100), devolviendo el valor correcto 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

y yo
fuente
@ Oriol - sí, tienes razón; Supongo que con lo que lo intenté anteriormente simplemente funcionó de esa manera debido a otros factores. Eliminaré esa pequeña nota en mi respuesta.
andi
1
no funciona:toFixed(Number.MAX_VALUE) == Number.MAX_VALUE
manonthemat
0
function printInt(n) { return n.toPrecision(100).replace(/\..*/,""); }

con algunos problemas:

  • 0.9 se muestra como "0"
  • -0.9 se muestra como "-0"
  • 1e100 se muestra como "1"
  • funciona solo para números hasta ~ 1e99 => use otra constante para números mayores; o más pequeño para la optimización.
Wiimm
fuente
Sí, esos son algunos problemas.
Adam Leggett
0

Destrozando las expresiones regulares. Esto no tiene problemas de precisión y no es mucho 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
fuente
-1

Si solo lo está haciendo para mostrar, puede construir una matriz a partir de los dígitos antes de que se redondeen.

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
fuente
Esto devolverá la respuesta correcta, pero tomará mucho tiempo y podría terminar para que la ejecución se agote.
Hannah puede
-1

Actualmente no existe una función nativa para disolver la notación científica. Sin embargo, para este propósito debe escribir su propia funcionalidad.

Aquí está mi:

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
fuente
Esto falla para números realmente grandes. Intenté 2830869077153280552556547081187254342445169156730 y obtuve 2830869077153280500000000000000000000000000000000
devolución de llamada el
-1

Puede usar el módulo exponencial from . Es ligero y totalmente probado.

import fromExponential from 'from-exponential';

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

También puede usar YourJS.fullNumber. Por ejemplo, YourJS.fullNumber(Number.MAX_VALUE)resulta en lo siguiente: 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

También funciona para números realmente pequeños. YourJS.fullNumber(Number.MIN_VALUE)devuelve esto: 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005

Es importante tener en cuenta que esta función siempre devolverá números finitos como cadenas pero devolverá números no finitos (por ejemplo, NaNo Infinity) como undefined.

Puede probarlo en la consola YourJS aquí .

Chris West
fuente
-3

Puedes usar number.toString(10.1):

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

Nota: actualmente funciona en Chrome, pero no en Firefox. La especificación dice que la raíz tiene que ser un número entero, por lo que esto resulta en un comportamiento poco confiable.

Zambonifofex
fuente
¿Qué diablos hace esto? ¿Una raíz de 10.1?
joniba
Esto no funciona Desde ES5, se requieren implementaciones para usar ToInteger en el argumento. Entonces usar 10.1 o 10o nada son equivalentes.
Oriol
Sí, esto ha dejado de funcionar recientemente. Un poco triste, pero esta respuesta ahora es irrelevante.
Zambonifofex
-6

Tuve el mismo problema con la devolución de la notación científica de Oracle, pero necesitaba el número real para una url. Acabo de usar un truco PHP restando cero, y obtengo el número correcto.

por ejemplo 5.4987E7 es el val.

newval = val - 0;

newval ahora es igual a 54987000

RaulQ
fuente
2
Esto no tiene efecto. 5.4987E7 tiene un exponente menor que 21, por lo que aparece como el número completo independientemente de cuándo se convierte en cadena. (5.4987E21 - 0) .toString () => "5.4987e + 21"
Dwight