Як уникнути наукових позначень для великої кількості JavaScript?


183

JavaScript перетворює великий INT в наукові позначення, коли число стає великим. Як я можу запобігти цьому?


1
Ви хочете, щоб 1E21 відображався як "1000000000000000000000"? Ви стурбовані тим, як відображається номер або як він зберігається?
outis

3
мене турбує, як це відображається: у мене є команда document.write (myvariable)
chris

1
мені потрібен номер як частина URL
chris

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

Вище використовується дешеве -n-легке повторення рядка ( (new Array(n+1)).join(str)). Ви можете визначити, String.prototype.repeatвикористовуючи російське селянське множення, і використовувати це замість цього.

Цю відповідь слід застосовувати лише до контексту питання: показ великої кількості без використання наукових позначень. Для всього іншого слід використовувати бібліотеку BigInt , таку як BigNumber , BigInt Leemon або 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");

Приклад:


Ваше рішення дає мені зовсім інший результат на 2 ^ 1000, ніж wolframalpha. Якісь покажчики?
Шейн Реустл

4
@Shane: Це запитання стосується відображення чисел з плаваючою комою у вигляді цілих чисел базових 10, а не адресних номерів, які не можуть бути представлені у форматі з плаваючою комою (що виникне при перетворенні на базу 10). Вам потрібна бібліотека JS bigint , про що йдеться у заключному рядку.
вихід

1
@PeterOlson: схожий на те, що я залишив а Math.abs. Дякую за голову.
outis

1
toFixed(Number.MAX_VALUE) == Number.MAX_VALUEповинна повертати вірно тоді, але це не ...
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

Це, мабуть, небажано, якщо в плановому результаті важлива точність.


2
Це нерозумна відповідь.
Арія

Це не працює у PhantomJS 2.1.1 - воно все ще залишається в науковій нотації. Чудово в Chrome.
Єгор Непомнящіх

1
Здається, це не працює для дуже малих десяткових знаків: var myNumb = 0,0000000001; console.log (myNumb.toLocaleString ('fullwide', {useGrouping: false}));
eh1160

Можна встановити параметр maxSignificantDigits (макс. 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
Egor

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, ви побачите, що вони не рівні.
манотематика

4
@manonthematica Звичайно, вони не рівні, тому що перший - це відформатований рядок, а другий - a Number. Якщо ви кинете перший на a Number, ви побачите, що вони абсолютно рівні: jsfiddle.net/qd6hpnyx/1 . Ви можете повернути свій голос назад:P
VisioN

досить чесно .... Я щойно помітив, що не можу, якщо ви не редагуєте свою відповідь.
манотехніка

Краще також замінити будь-який трейлінг .0порожнім рядком.
vsync

Я отримую дещо дивне значення, коли намагаюся console.log(2.2e307.toFixedSpecial(10)). Я маю на увазі ... я отримую кілька нульових нулів. Оголошення все-таки тому, що це здається найближчим до того, що мені потрібно.
peter.petrov

6

Наступне рішення обходить автоматичне експоненціальне форматування для дуже великих і дуже малих чисел. Це рішення outis з помилкою : він не працює для дуже малих негативних чисел.

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
числоToString (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;
}

Можливо, буде корисна деяка додаткова інформація щодо відповіді на цей код.
Pyves

Коментарі додані до функції.

2

Використання .toPrecisionі .toFixedт. Д. Ви можете порахувати кількість цифр у своєму номері, перетворивши його в рядок, .toStringпотім переглянувши його .length.


чомусь, точність не працює. якщо ви спробуєте: window.examplenum = 1352356324623461346, а потім скажіть попередження (window.examplenum.toPrecision (20)), воно не з’явиться сповіщення
chris

насправді він спливає, показуючи наукові позначення, а в інший час зовсім не з’являється. що я роблю неправильно?
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;
  }
}

1

Ви можете перевести номер на номер і досягти округлення

// функціональність для заміни char у заданому індексі

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, і виникають проблеми при використанні журналу замість log10 у рядку перед циклом for; він буде писати 1000 в base-10, як 000, тому я змінив його на log10, оскільки люди в основному використовують базову-10 у будь-якому випадку.

Це може бути не дуже точним рішенням, але я з гордістю можу сказати, що він може успішно переводити цифри по базах і поставляється з можливістю коми!


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

добре, будьте уважні, що bignumber.js не може перевищувати 15 значущих цифр ...
Майкл Гейбергер

0

Я знаю, що це через багато років, але я нещодавно працював над подібним питанням і хотів опублікувати своє рішення. В даний час прийнята відповідь викреслює експонентну частину з 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


@Oriol - так, ти маєш рацію; Я здогадуюсь, що б я не пробував із цим, що раніше працював таким чином через інші фактори. Я видалю цю маленьку записку у своїй відповіді.
andi

1
не працює:toFixed(Number.MAX_VALUE) == Number.MAX_VALUE
манотехніка

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

з деякими питаннями:

  • 0,9 відображається як "0"
  • -0.9 відображається як "-0"
  • 1e100 відображається як "1"
  • працює лише для чисел до ~ 1e99 => використовувати інші константи для більших чисел; або менше для оптимізації.

Так, це деякі питання.
Адам Леггетт

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 і отримав 28308690771532805000000000000000000000000000000000000
відкликання


-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. У специфікації зазначено, що радіус повинен бути цілим числом, тому це призводить до ненадійної поведінки.


що чорт це робить? Радікс 10,1?
joniba

Це не працює. Починаючи з ES5, для введення в аргумент потрібно використовувати ToInteger. Тож використання 10.1 або 10нічого не рівнозначне.
Оріол

Так, останнім часом це справді перестало працювати. Як ні сумно, але ця відповідь зараз не має значення.
Zambonifofex

-6

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

наприклад 5,4987E7 - це val.

newval = val - 0;

newval зараз дорівнює 54987000


2
Це не має ефекту. 5.4987E7 має показник менше 21, тому він відображається як повне число незалежно від перетворення в рядок. (5.4987E21 - 0) .toString () => "5.4987e + 21"
Дуайт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.