Как избежать научного обозначения больших чисел в JavaScript?

183

JavaScript преобразует большой INT в научную запись, когда число становится большим. Как я могу предотвратить это?

Крис
источник
1
Вы хотите, чтобы 1E21 отображался как «1000000000000000000000»? Вы обеспокоены тем, как отображается номер или как он хранится?
outis
3
я обеспокоен тем, как это отображается: у меня есть команда document.write (myvariable)
chris
1
мне нужен номер как часть URL
Крис
3
@outis: Люди-люди не единственные, кто хочет читать цифры. Кажется, что D3 сгенерирует исключение при обнаружении translateпреобразования, которое содержит координаты в научной нотации.
ИЛИ Mapper
2
Это все еще полезно, когда вы имеете дело с числами до определенного размера. Научная нотация будет трудна для чтения пользователям, которые в конце концов не знают и научной нотации.
Hippietrail

Ответы:

110

Есть Number.toFixed , но он использует научную запись, если число> = 1e21 и имеет максимальную точность 20. Кроме этого, вы можете бросить свой собственный, но это будет грязно.

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

Выше используется дешевая -nn-easy строка repetition ( (new Array(n+1)).join(str)). Вы можете определить String.prototype.repeatиспользование русского крестьянского умножения и использовать его вместо этого.

Этот ответ должен применяться только к контексту вопроса: отображение большого числа без использования научных обозначений. Для всего остального вы должны использовать библиотеку BigInt , такую ​​как BigNumber , Leemon's BigInt или BigInteger . В будущем должен быть доступен новый родной BigInt (примечание: не Leemon's); Chromium и браузеры на его основе ( Chrome , новый Edge [v79 +], Brave ) и Firefox имеют поддержку; Поддержка Safari продолжается.

Вот как бы вы использовали BigInt для этого: BigInt(n).toString()

Пример:

Осторожно , однако, что любое целое число выводе как число JavaScript (не BigInt) , что больше , чем 15-16 цифр ( в частности, больше , чем Number.MAX_SAFE_INTEGER + 1[9.007.199.254.740.992]) , может быть быть закруглены, так как тип номера в JavaScript (IEEE-754 с двойной точностью с плавающей точкой) не может точно содержать все целые числа за этой точкой. Поскольку Number.MAX_SAFE_INTEGER + 1он работает с коэффициентами, кратными 2, то он больше не может содержать нечетные числа (и, аналогично, с 18 014 398 509 481 984 он начинает работать с коэффициентами, кратными 4, затем 8, 16, ...).

Следовательно, если вы можете рассчитывать на BigIntподдержку, выведите свой номер в виде строки, которую вы передаете BigIntфункции:

const n = BigInt("YourNumberHere");

Пример:

outis
источник
Ваше решение дает мне совершенно другой результат на 2 ^ 1000, чем вольфрамальфа. Есть указатели?
Шейн Рустл
4
@Shane: В этом разделе «Вопросы и ответы» речь идет об отображении чисел с плавающей запятой в виде целых чисел с основанием 10, и они не относятся к числам, которые невозможно представить в формате с плавающей запятой (что возникнет при преобразовании в основание 10). Вам нужна библиотека JS bigint , как указано в последней строке.
outis
1
@PeterOlson: похоже, я не учел Math.abs. Спасибо за хедз-ап.
outis
1
toFixed(Number.MAX_VALUE) == Number.MAX_VALUEдолжен вернуть true тогда, но это не так ...
manonthemat
3
На самом деле этот код, как он есть, не работает для очень маленьких отрицательных чисел: toFixed (-1E-20) -> "0.0000000000000000000.09999999999999999"
davidthings
44

Я знаю, что это старый вопрос, но в последнее время показывает активный. 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"

Вы можете использовать параметры для форматирования вывода.

Примечание:

Number.toLocaleString () округляет после 16 десятичных знаков, так что ...

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

... возвращается ...

586084736227728400000000000000000000000

Это, возможно, нежелательно, если точность важна в намеченном результате.

flapjack17
источник
2
Это надежный ответ.
Ария
Это не работает в PhantomJS 2.1.1 - оно все еще заканчивается научной нотацией. Хорошо в Chrome.
Егор Непомнящих
1
Это не работает для очень маленьких десятичных знаков: var myNumb = 0.0000000001; console.log (myNumb.toLocaleString ('fullwide', {useGrouping: false}));
eh1160
Можно установить параметр MaximumSignificantDigits (максимум 21) для форматирования очень маленьких десятичных знаков, то есть:js console.log( myNumb.toLocaleString('fullwide', { useGrouping: true, maximumSignificantDigits:6}) );
Сэр. MaNiAl
20

Для небольшого числа, и вы знаете, сколько десятичных разрядов вы хотите, вы можете использовать toFixed, а затем использовать регулярное выражение, чтобы удалить конечные нули.

Number(1e-7).toFixed(8).replace(/\.?0+$/,"") //0.000
Никлас Форсстрём
источник
26
вопрос буквально задавали для большого числа
swyx
1
Будьте осторожны, чтобы избежать 0 в качестве аргумента вызова toFixed - это приведет к стиранию значительных конечных нулей:(1000).toFixed(0).replace(/\.?0+$/,"") // 1, not 1000
Егор Непомнящих
15

еще одно возможное решение:

function toFix(i){
 var str='';
 do{
   let a = i%10;
   i=Math.trunc(i/10);
   str = a+str;
 }while(i>0)
 return str;
}
Миродил
источник
7
Это не сохраняет первоначальное значение ... например, 31415926535897932384626433832795 становится 31415926535897938480804068462624
zero_cool
Не очень хорошо, но, вероятно, связано с тем, как JavaScript обрабатывает большие числа.
zero_cool
@domsson В основном потому, что применяется арифметика чисел с плавающей точкой IEEE. числа в Javascript на самом деле представлены как числа с плавающей запятой, а также как «десятичные числа», нет собственного «целочисленного точного типа». Вы можете прочитать здесь: medium.com/dailyjs/javascripts-number-type-8d59199db1b6
Pac0
@zero_cool Любое число больше, чем Number.MAX_SAFE_INTEGER, вероятно, страдает этой проблемой.
Pac0
10

Вот мой короткий вариант Number.prototype.toFixedметода, который работает с любым числом:

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"

зрение
источник
если вы сравните Number.MAX_VALUE.toFixedSpecial (2) с Number.MAX_VALUE, вы увидите, что они не равны.
Manonthemat
4
@manonthemat Конечно, они не равны, потому что первая - это отформатированная строка, а вторая - Number. Если вы Numberприведете первое к a , вы увидите, что они абсолютно равны: jsfiddle.net/qd6hpnyx/1 . Вы можете забрать обратно свой голос:P
VisioN
достаточно справедливо .... я только заметил, что не могу, если вы не редактируете свой ответ.
Manonthemat
Лучше также заменить любой трейлинг .0на пустую строку.
Вс
Я получаю несколько странное значение, когда я пытаюсь console.log(2.2e307.toFixedSpecial(10)). Я имею в виду ... Я получаю несколько конечных нулей. Во всяком случае, от голосования, потому что это кажется самым близким к тому, что мне нужно.
peter.petrov
6

Следующее решение обходит автоматическое экспоненциальное форматирование для очень больших и очень маленьких чисел. Это наше решение с исправлением: оно не работало для очень маленьких отрицательных чисел.

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 Это пресловутая проблема точности с плавающей точкой в Javascript , которая представляет собой еще одну совершенно другую проблему при использовании чисел в JS ... См., например, stackoverflow.com/questions/1458633/…
user56reinstatemonica8
3

Ответы других не дают вам точного числа!
Эта функция точно вычисляет желаемое число и возвращает его в строке, чтобы предотвратить его изменение с помощью javascript!
Если вам нужен числовой результат, просто умножьте результат функции на номер один!

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

источник
Некоторая дополнительная информация об этом кодовом ответе, вероятно, будет полезна.
Пивес
Комментарии добавлены в функцию.
2

Использование .toPrecision, .toFixedи т.д. Вы можете подсчитать количество цифр в вашем номере путем преобразования его в строку с .toStringпотом , глядя на ее .length.


источник
по какой-то причине toPrecision не работает. если вы попробуете: window.examplenum = 1352356324623461346, а затем произнесите alert (window.examplenum.toPrecision (20)), предупреждение не появится
chris
на самом деле это всплывает, иногда показывая научную запись, а иногда это не всплывает вообще. Что я делаю не так?
Крис
18
Ни один из методов предложения не работает для больших (или малых) чисел. (2e64).toString()вернется "2e+64", так .lengthчто бесполезно.
CodeManX
2

Это то, что я использовал, чтобы взять значение из ввода, расширив числа менее 17 цифр и преобразовав экспоненциальные числа в 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
источник
1

Вы можете зациклить число и достичь округления

// функциональность для замены символа по указанному индексу

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

// цикл по числу начинается

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

Я попытался работать со строковой формой, а не с числом, и это, похоже, сработало. Я проверял это только на Chrome, но он должен быть универсальным:

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

Я думаю, что может быть несколько похожих ответов, но вот что я придумал

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

Примечание: это так же точно, как математические функции javascript и имеет проблемы при использовании log вместо log10 в строке перед циклом for; он будет записывать 1000 в base-10 как 000, поэтому я изменил его на log10, потому что люди в основном будут использовать base-10 в любом случае.

Возможно, это не очень точное решение, но я с гордостью могу сказать, что оно может успешно переводить числа между базами и поставляется с опцией для запятых!

cmarangu
источник
1

Ваш вопрос:

number :0x68656c6c6f206f72656f
display:4.9299704811152646e+23

Вы можете использовать это: https://github.com/MikeMcl/bignumber.js

Библиотека JavaScript для десятичной и недесятичной арифметики произвольной точности.

как это:

let ten =new BigNumber('0x68656c6c6f206f72656f',16);
console.log(ten.toString(10));
display:492997048111526447310191
super2bai
источник
хорошо, остерегайтесь того, что bignumber.js не может превышать 15 значащих цифр ...
Майкл Хойбергер,
0

Я знаю, что это много лет спустя, но недавно я работал над аналогичной проблемой и хотел опубликовать свое решение. В настоящее время принятый ответ дополняет часть экспоненты нулями, и моя попытка найти точный ответ, хотя в целом он не совсем точен для очень больших чисел из-за ограничения JS в точности с плавающей запятой.

Это работает Math.pow(2, 100), возвращая правильное значение 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

и я
источник
@ Ориоль - да, ты прав; Я предполагаю, что все, что я пробовал ранее, получалось именно так из-за других факторов. Я удалю эту небольшую записку в своем ответе.
Анди
1
не работает:toFixed(Number.MAX_VALUE) == Number.MAX_VALUE
manonthemat
0
function printInt(n) { return n.toPrecision(100).replace(/\..*/,""); }

с некоторыми проблемами:

  • 0,9 отображается как «0»
  • -0,9 отображается как "-0"
  • 1e100 отображается как «1»
  • работает только для чисел до ~ 1e99 => использовать другую константу для больших чисел; или меньше для оптимизации.
Wiimm
источник
Да, это некоторые проблемы.
Адам Леггетт
0

Вырвать регулярные выражения. Это не имеет проблем с точностью и не очень много кода.

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

Если вы просто делаете это для отображения, вы можете построить массив из цифр, прежде чем они будут округлены.

var num = Math.pow(2, 100);
var reconstruct = [];
while(num > 0) {
    reconstruct.unshift(num % 10);
    num = Math.floor(num / 10);
}
console.log(reconstruct.join(''));
Джейк М
источник
Это вернет правильный ответ, но займет так много времени и может закончиться тайм-аутом выполнения.
Ханна
-1

В настоящее время нет нативной функции растворения научной нотации. Однако для этого вы должны написать свой собственный функционал.

Вот мой:

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("");
}
Мартин Уитке
источник
Это не удается для действительно больших чисел. Я попытался 2830869077153280552556547081187254342445169156730 и получил 283086907715328050000000000000000000000000000000000000000000000
обратный вызов
-1

Вы можете использовать из экспоненциального модуля. Он легкий и полностью протестирован.

import fromExponential from 'from-exponential';

fromExponential(1.123e-10); // => '0.0000000001123'
shrpne
источник
-1

Вы также можете использовать YourJS.fullNumber. Например, YourJS.fullNumber(Number.MAX_VALUE)приводит к следующему: 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Это также работает для действительно небольших чисел. YourJS.fullNumber(Number.MIN_VALUE)возвращает это: 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005

Важно отметить, что эта функция всегда будет возвращать конечные числа как строки, но будет возвращать неконечные числа (например, NaNили Infinity) как undefined.

Вы можете проверить это в консоли YourJS здесь .

Крис Вест
источник
-3

Вы можете использовать number.toString(10.1):

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

Примечание. В настоящее время это работает в Chrome, но не в Firefox. Спецификация говорит, что основание должно быть целым числом, так что это приводит к ненадежному поведению.

Zambonifofex
источник
какого черта это делает? Основание 10,1?
Джониба
Это не работает Начиная с ES5, реализации должны использовать ToInteger для аргумента. Так что использование 10.1 или 10ничего не эквивалентно.
Ориоль
Да, это действительно перестало работать в последнее время. Как-то грустно, но этот ответ сейчас не имеет значения.
Zambonifofex
-6

У меня была та же проблема с оракулом, возвращающим научную нотацию, но мне нужно было фактическое число для URL. Я просто использовал трюк PHP, вычитая ноль, и я получил правильное число.

например 5.4987E7 является val.

newval = val - 0;

newval теперь равен 54987000

RaulQ
источник
2
Это не имеет никакого эффекта. 5.4987E7 имеет показатель степени меньше 21, поэтому он отображается как полное число независимо от того, когда он конвертируется в строку. (5.4987E21 - 0) .toString () => "5.4987e + 21"
Дуайт