Чи підтримує JavaScript 64-розрядні цілі числа?


81

У мене є такий код:

var str = "0x4000000000000000";   //4611686018427387904 decimal
var val = parseInt(str);
alert(val);

Я отримую таке значення: " 4611686018427388000", яке є0x4000000000000060

Мені цікаво, чи JavaScript неправильно обробляє 64-розрядні цілі числа, чи я роблю щось не так?



Відповіді:


88

JavaScript представляє числа, що використовують формат подвійної точності (64 біт) IEEE-754 . Наскільки я розумію, це дає точність до 53 бітів, або від п’ятнадцяти до шістнадцяти знаків після коми. Ваш номер має більше цифр, ніж JavaScript може впоратись, тож ви отримаєте наближення.

Це насправді не є "неправильним поводженням" як таке, але, очевидно, це не дуже корисно, якщо вам потрібна повна точність великих цифр. Навколо є кілька бібліотек JS, які можуть обробляти більші числа, наприклад, BigNumber та Int64 .


2
Довгий час може допомогти і goog.math.Закриття
Джеремі

39
Слід додати, що операції на рівні бітів обмежуються 32-бітовим IIUC.
sellibitze

2
goog.math.Довга документація переміщена: google.github.io/closure-library/api/class_goog_math_Long.html
benizi

5
(Коментар @Michaelangelo ) На жаль, специфікації ECMAScript 2015 (версія 6) не мають офіційної підтримки UInt64; в той час як Mozilla додала підтримку UInt64 - це нестандартно. WebGL має подібні потреби, але, на жаль, їх немає Uint64Array, лише Uint32Array .
falsarella

2
Документація goog.math.Long знову перемістилася: google.github.io/closure-library/api/goog.math.Long.html (Дякую, @Pacerier)
benizi

11

Chromium версії 57 та пізніших версій підтримує цілі числа довільної точності. Це називається BigInt, і над ним працюють також для інших браузерів. Це значно швидше, ніж реалізація JavaScript.


Також підтримується Opera 54+ та Node.js. Firefox 65+ підтримує це, якщо javascript.options.bigintпрапорець увімкнено.
Девід

Це не завжди швидше. порівняйте це console.time("go");for (var i=0;i<10000000;++i) {} console.timeEnd("go");з 64- console.time("go");for (var i=0n;i<10000000n;++i) {} console.timeEnd("go");
бітними

@ CiboFATA8: Він говорить про BigInt як про компонент власного браузера проти BigInt, реалізований у JavaScript. Ви порівнюєте числа js, які є плаваючими з точністю близько 53 біт (не 64), із власним браузером BigInt (також не 64 біт).
hippietrail

0

Тобто, V8 JavaScript - це похідний механізм Smalltalk. (1980-ті - дотепер) Двигуни Lisp та Smalltalk підтримують багатоточну арифметику з використанням <LargeInteger>, яке іноді називають <BigInt>. Спойлер, команда Dart в Google - це, в основному, купа колишніх Smalltalker, які об'єднують свій досвід у просторі JS.

Ці типи чисел мають необмежену точність і, як правило, використовуються як будівельні блоки для надання об'єктів <Rational: Fraction>, чисельник і знаменник яких може бути будь-яким типом числа, включаючи <BigInt>. Завдяки цьому можна зобразити дійсні числа, уявні числа, і робити це з досконалою точністю на ірраціональних числах, таких як (1/3).

Примітка: Я давно впроваджую та розроблюю Smalltalk, JS та інші мови та їх механізми та фреймворки.

Якщо це зробити належним чином <BigInt> для мультиточної арифметики як стандартної функції JavaScript, це відкриє двері до величезного набору операцій, включаючи нативну ефективну криптографію (що легко зробити за допомогою багатоточних чисел).

Наприклад, в одному з моїх двигунів smalltalk 1998 року на процесорі 2,3 ГГц я щойно працював:

[10000 factorial] millisecondsToRun => 59ms
10000 factorial asString size => 35660 digits

[20000 factorial] millisecondsToRun => 271ms
20000 factorial asString size => 77338 digits

Визначається як: (ілюструє <BigInt>багатоточність у дії)

factorial

   "Return the factorial of <self>."

   | factorial n |

    (n := self truncate) < 0 ifTrue: [^'negative factorial' throw].
    factorial := 1.
    2 to: n do:
    [:i |
        factorial := factorial * i.
    ].
   ^factorial

Робота двигуна V8 від Ларса Бака (сучасника моєї роботи) походить від Animorphic Smalltalk з роботи САМД Девіда Унгара, похідної від Smalltalk-80, а згодом переросла в JVM і перероблена Lars for Mobile, що з'явилася пізніше як основа двигуна V8.

Я згадую це тому, що і Animorphic Smalltalk, і QKS Smalltalk підтримують анотації типів, які дозволяють двигуну та інструментам міркувати про код подібним чином, який TypeScript робив спробу для JavaScript.

Цей натяк на анотації та його використання мовою, інструментами та механізмами виконання дає можливість підтримувати мультиметоди (а не подвійне відправлення), необхідні для належної підтримки мультиточних арифметичних правил просування та примусу.

Що, в свою чергу, є ключовим для підтримки 8/16/32/64 int / uints та багатьох інших числових типів у когерентній структурі.

<Magnitude|Number|UInt64>Приклади багато методів з QKS Smalltalk 1998

Integer + <Integer> anObject

   "Handle any integer combined with any integer which should normalize
    away any combination of <Boolean|nil>."
   ^self asInteger + anObject asInteger

-- multi-method examples --

Integer + <Number> anObject

   "In our generic form, we normalize the receiver in case we are a
    <Boolean> or <nil>."
   ^self asInteger + anObject

-- FFI JIT and Marshaling to/from <UInt64>

UInt64 ffiMarshallFromFFV
   |flags| := __ffiFlags(). 
   |stackRetrieveLoc| := __ffiVoidRef().
    ""stdout.printf('`n%s [%x]@[%x] <%s>',thisMethod,flags,stackRetrieveLoc, __ffiIndirections()).
    if (flags & kFFI_isOutArg) [
        "" We should handle [Out],*,DIM[] cases here
        "" -----------------------------------------
        "" Is this a callout-ret-val or a callback-arg-val
        "" Is this a UInt64-by-ref or a UInt64-by-val
        "" Is this an [Out] or [InOut] callback-arg-val that needs 
        ""   to be updated when the callback returns, if so allocate callback
        ""   block to invoke for doing this on return, register it as a cleanup hook.
    ].
   ^(stackRetrieveLoc.uint32At(4) << 32) | stackRetrieveLoc.uint32At(0).

-- <Fraction> --

Fraction compareWith: <Real> aRealValue

   "Compare the receiver with the argument and return a result of 0
    if the received <self> is equal, -1 if less than, or 1 if
    greater than the argument <anObject>."
   ^(numerator * aRealValue denominator) compareWith:
            (denominator * aRealValue numerator)

Fraction compareWith: <Float> aRealValue

   "Compare the receiver with the argument and return a result of 0
    if the received <self> is equal, -1 if less than, or 1 if
    greater than the argument <anObject>."
   ^self asFloat compareWith: aRealValue

-- <Float> --

Float GetIntegralExpAndMantissaForBase(<[out]> mantissa, <const> radix, <const> mantissa_precision)
   |exp2| := GetRadix2ExpAndMantissa(&mantissa).
    if(radix = 2) ^exp2.

   |exp_scale| := 2.0.log(radix).
   |exp_radix| := exp2 * exp_scale.
   |exponent| := exp_radix".truncate".asInteger.
    if ((|exp_delta| := exp_radix - exponent) != 0) [
       |radix_exp_scale_factor| := (radix.asFloat ^^ exp_delta).asFraction.
        "" Limit it to the approximate precision of a floating point number
        if ((|scale_limit| := 53 - mantissa.highBit - radix.highBit) > 0) [
            "" Compute the scaling factor required to preserve a reasonable
            "" number of precision digits affected by the exponent scaling 
            "" roundoff losses. I.e., force mantissa to roughly 52 bits
            "" minus one radix decimal place.
           |mantissa_scale| := (scale_limit * exp_scale).ceiling.asInteger.     
            mantissa_scale timesRepeat: [mantissa :*= radix].
            exponent :-= mantissa_scale.
        ] else [
            "" If at the precision limit of a float, then check the
            "" last decimal place and follow a rounding up rule
            if(exp2 <= -52 and: [(mantissa % radix) >= (radix//2)]) [
                mantissa := (mantissa // radix)+1.
                exponent :+= 1.
            ].
        ].
        "" Scale the mantissa by the exp-delta factor using fractions
        mantissa := (mantissa * radix_exp_scale_factor).asInteger.
    ].

    "" Normalize to remove trailing zeroes as appropriate
    while(mantissa != 0 and: [(mantissa % radix) = 0]) [
        exponent :+= 1.
        mantissa ://= radix.
    ].
   ^exponent.

Я би очікував, що деякі подібні шаблони почнуть з'являтися для підтримки JavaScript UIn64 / Int64 та інших структурних або числових типів, коли <BigInt> розвивається.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.