Как избежать научного обозначения больших чисел в JavaScript?
JavaScript преобразует большой INT в научную запись, когда число становится большим. Как я могу предотвратить это?
28 ответов
Есть 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;
}
Выше используется дешёвое и легкое повторение строк ((new Array(n+1)).join(str)
). Вы могли бы определить String.prototype.repeat
с помощью русского крестьянского умножения и использовать его вместо этого.
Этот ответ должен применяться только к контексту вопроса: отображение большого числа без использования научных обозначений. Для всего остального вы должны использовать библиотеку BigInt, такую как BigNumber, Leemon's BigInt или BigInteger. В дальнейшем предлагаемый BigInt (примечание: не от Leemon) должен поддерживаться изначально ( Chrome имеет поддержку, и он работает над Firefox).
Я знаю, что это старый вопрос, но в последнее время показывает активный. 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
Это, возможно, нежелательно, если точность важна в намеченном результате.
Для небольшого числа, и вы знаете, сколько десятичных знаков вы хотите, вы можете использовать toFixed, а затем использовать регулярное выражение, чтобы удалить завершающие нули.
Number(1e-7).toFixed(8).replace(/\.?0+$/,"") //0.000
Вопрос поста заключался в том, чтобы избежать цифровых обозначений и иметь номер в виде простого числа.
Следовательно, если все, что нужно, - это преобразовать числа в электронной (научной) нотации в простые числа (в том числе в случае дробных чисел) без потери точности, то важно избегать использования
Math
object и другие числовые методы javascript, чтобы округление не происходило при обработке больших чисел и больших дробей (что всегда происходит из-за внутренней памяти в двоичном формате).
Следующая функция преобразует числа в электронной (научной) нотации в простые числа (включая дроби), обрабатывая как большие числа, так и большие дроби, без потери точности, поскольку она не использует встроенные математические и числовые функции для обработки числа или управления им.
Функция также обрабатывает обычные числа, так что число, которое, как предполагается, будет иметь обозначение "e", можно передать функции для исправления.
Функция должна работать с десятичными точками в разных языковых стандартах.
Предоставляется 94 тестовых примера.
Для больших чисел электронной записи число передается в виде строки.
Примеры:
eToNumber("123456789123456789.111122223333444455556666777788889999e+50");
// output:
"12345678912345678911112222333344445555666677778888999900000000000000"
eToNumber("123.456123456789123456895e-80");
// output:
"0.00000000000000000000000000000000000000000000000000000000000000000000000000000123456123456789123456895"
eToNumber("123456789123456789.111122223333444455556666777788889999e-50");
// output:
"0.00000000000000000000000000000000123456789123456789111122223333444455556666777788889999"
Допустимые номера электронной записи в Javascript включают следующее:
123e1 ==> 1230
123E1 ==> 1230
123e+1 ==> 1230
123.e+1 ==> 1230
123e-1 ==> 12.3
0.1e-1 ==> 0.01
.1e-1 ==> 0.01
-123e1 ==> -1230
Удаление регулярных выражений. Это не имеет проблем с точностью и требует небольшого количества кода.
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));
Еще одно возможное решение:
function toFix(i){
var str='';
do{
let a = i%10;
i=Math.trunc(i/10);
str = a+str;
}while(i>0)
return str;
}
Вот мой короткий вариант Number.prototype.toFixed
метод, который работает с любым числом:
Number.prototype.toFixedSpecial = function(n) {
var str = this.toFixed(n);
if (str.indexOf('e+') < 0)
return str;
// if number is in scientific notation, pick (b)ase and (p)ower
return str.replace('.', '').split('e+').reduce(function(p, b) {
return p + Array(b - p.length + 2).join(0);
}) + '.' + Array(n + 1).join(0);
};
1e21.toFixedSpecial(2); // "1000000000000000000000.00"
2.1e24.toFixedSpecial(0); // "2100000000000000000000000"
1234567..toFixedSpecial(1); // "1234567.0"
1234567.89.toFixedSpecial(3); // "1234567.890"
Следующее решение обходит автоматическое экспоненциальное форматирование для очень больших и очень маленьких чисел. Это наше решение с исправлением: оно не работало для очень маленьких отрицательных чисел.
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));
Вы можете использовать от-экспоненциальный модуль. Он легкий и полностью протестирован.
import fromExponential from 'from-exponential';
fromExponential(1.123e-10); // => '0.0000000001123'
Используйте эту функцию:
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 absolute the 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;
}
Это то, что я использовал, чтобы получить значение из входных данных, увеличивая числа менее 17 цифр и преобразовывая экспоненциальные числа в x10y
// 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;
}
}
Ваш вопрос:
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
Использование .toPrecision
, .toFixed
и т. д. Вы можете посчитать количество цифр в вашем числе, преобразовав его в строку с .toString
затем, глядя на его .length
,
Мне это не помогло:
console.log( myNumb.toLocaleString('fullwide', {useGrouping:false}) );
но это:
value.toLocaleString("fullwide", {
useGrouping: false,
maximumSignificantDigits: 20,
})
Я знаю, что это много лет спустя, но я недавно работал над аналогичной проблемой, и я хотел опубликовать свое решение. В настоящее время принятый ответ дополняет часть экспоненты нулями, и моя попытка найти точный ответ, хотя в целом он не совсем точен для очень больших чисел из-за ограничения 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
Я только что провел тест на своем Chrome 113, чтобы сравнить
-
BigInt(integer).toString()
- и
integer.toLocaleString("fullwide", {useGrouping: false})
.
a = BigInt(Math.floor(Math.random() * 1e20)).toString();
a = Math.floor(Math.random() * 1e20).toLocaleString("fullwide", {useGrouping: false});
Созданиеbigint
внеnumber
и его преобразование в строку оказалось более чем в сто раз быстрее, чем .
Также обратите внимание, чтоBigInt(-0)
отбрасывает знак, в отличие отtoLocaleString
это сохраняет его.
Вы можете зациклить число и достичь округления
// функциональность для замены символа по указанному индексу
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--;
}
}
Я пытался работать с строковой формой, а не с числом, и это, похоже, сработало. Я проверял это только на 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;
}
Я думаю, что может быть несколько похожих ответов, но вот что я придумал
// 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 в любом случае.
Возможно, это не очень точное решение, но я с гордостью могу сказать, что оно может успешно переводить числа между базами и поставляется с опцией для запятых!
Попробуй это:
Number.standardizenumber = function (number,n) {
var mantissa = number.toLocaleString(
'en-US', {
useGrouping: false,
signDisplay: "never",
notation: "scientific",
minimumFractionDigits: 16,
maximumFractionDigits: 16
}
).toLowerCase().split('e')[0].replace(/\./g,'');
var exponentNegative = "0".repeat(Math.max(+Math.abs(number).toExponential().toLowerCase().split('e-')[1]-1,0)) + mantissa;
var exponentPositive = Math.abs(number)<1E17?mantissa.slice(0,+Math.abs(number).toExponential().toLowerCase().split('e+')[1]+1):mantissa+(Math.abs(number).toExponential().toLowerCase().split('e+')[1]-16);
var decimalExpPositive = Math.abs(number)<1E17?mantissa.slice(0,Math.abs(number).toExponential().toLowerCase().split('e+')[0]-16):undefined;
var fullDec = number===0?(1/number<0?'-0':'0'):(1/Math.sign(number)<0?'-':'')+(Math.abs(number)>=1?[exponentPositive,(number%1===0?(decimalExpPositive.slice(+Math.abs(number).toExponential().toLowerCase().split('e+')[1]+1)): undefined)].join('.'):`.${exponentNegative}`);
return isNaN(number)===false&&Math.abs(number)<1E17?((number%1===0?number.toLocaleString('en-US', {useGrouping: false}):fullDec).includes('.')===false?fullDec.split('.')[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","):fullDec.replace(/(\.[0-9]*[1-9])0+$|\.0*$/,'$1').replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",")):number.toLocaleString('en-US');
}
Number.standardizenumber(.0000001) // .0000001
Number.standardizenumber(1E21) // 1,000,000,000,000,000,000,000
Number.standardizenumber(1_234_567_890.123456) // 1,234,567,890.123456
В настоящее время нет нативной функции по растворению научной нотации. Однако для этого вы должны написать свой собственный функционал.
Вот мой:
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("");
}
Вы также можете использовать YourJS.fullNumber. Например YourJS.fullNumber(Number.MAX_VALUE)
результаты в следующем:179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Это также работает для действительно небольших чисел. YourJS.fullNumber(Number.MIN_VALUE)
возвращает это:0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005
Важно отметить, что эта функция всегда будет возвращать конечные числа в виде строк, но будет возвращать не конечные числа (например, NaN
или же Infinity
) как undefined
,
Вы можете проверить это в консоли YourJS здесь.
Если вы хотите преобразовать научное представление в целое число:
parseInt("5.645656456454545e+23", 10)
результат:
5
Если вы не возражаете против использования Lodash, у него есть toSafeInteger()
_.toSafeInteger(3.2);
// => 3
_.toSafeInteger(Number.MIN_VALUE);
// => 0
_.toSafeInteger(Infinity);
// => 9007199254740991
_.toSafeInteger('3.2');
// => 3
Если вы просто делаете это для отображения, вы можете построить массив из цифр, прежде чем они будут округлены.
var num = Math.pow(2, 100);
var reconstruct = [];
while(num > 0) {
reconstruct.unshift(num % 10);
num = Math.floor(num / 10);
}
console.log(reconstruct.join(''));
function printInt(n) { return n.toPrecision(100).replace(/\..*/,""); }
с некоторыми проблемами:
- 0,9 отображается как "0"
- -0,9 отображается как "-0"
- 1e100 отображается как "1"
- работает только для чисел до ~1e99 => использовать другую константу для больших чисел; или меньше для оптимизации.
Ты можешь использовать number.toString(10.1)
:
console.log(Number.MAX_VALUE.toString(10.1));
Примечание. В настоящее время это работает в Chrome, но не в Firefox. Спецификация говорит, что основание должно быть целым числом, так что это приводит к ненадежному поведению.
У меня была та же проблема с оракулом, возвращающим научную нотацию, но мне нужен был фактический номер для URL. Я просто использовал трюк PHP, вычитая ноль, и я получил правильное число.
например 5.4987E7 является val.
newval = val - 0;
newval теперь равен 54987000