Mam następujący kod:
var str = "0x4000000000000000"; //4611686018427387904 decimal
var val = parseInt(str);
alert(val);
Otrzymuję tę wartość: „ 4611686018427388000
”, czyli0x4000000000000060
Zastanawiałem się, czy JavaScript źle obsługuje 64-bitowe liczby całkowite, czy też robię coś źle?
javascript
numbers
integer
64-bit
ahmd0
źródło
źródło
Odpowiedzi:
JavaScript reprezentuje liczby w formacie IEEE-754 podwójnej precyzji (64-bitowy). Jak rozumiem, daje to dokładność 53 bitów lub piętnaście do szesnastu cyfr dziesiętnych. Twój numer ma więcej cyfr, niż może obsłużyć JavaScript, więc otrzymujesz przybliżenie.
Nie jest to tak naprawdę „błędna obsługa” jako taka, ale oczywiście nie jest zbyt pomocna, jeśli potrzebujesz pełnej precyzji na dużych liczbach. Istnieje kilka bibliotek JS, które mogą obsługiwać większe liczby, np. BigNumber i Int64 .
źródło
UInt64
; podczas gdy Mozilla dodała obsługę UInt64 - jest to niestandardowe. WebGL ma podobne potrzeby, ale niestety ich nie maUint64Array
, tylko Uint32Array .Chromium w wersji 57 i nowszych natywnie obsługuje liczby całkowite o dowolnej precyzji. Nazywa się to BigInt i pracuje nad tym również dla innych przeglądarek. Jest znacznie szybszy niż implementacje JavaScript.
źródło
javascript.options.bigint
flaga jest włączona.console.time("go");for (var i=0;i<10000000;++i) {} console.timeEnd("go");
liczbami 64console.time("go");for (var i=0n;i<10000000n;++i) {} console.timeEnd("go");
Tj. V8 JavaScript jest silnikiem wywodzącym się z Smalltalk. (Lata 80-te - obecnie) Silniki Lisp i Smalltalk obsługują arytmetykę o dużej precyzji przy użyciu <LargeInteger>, czasami nazywanego <BigInt>. Spoiler, zespół Dart w Google to w dużej mierze grupa byłych Smalltalkerów, którzy wspólnie przenoszą swoje doświadczenie w przestrzeń JS.
Te typy liczb mają nieograniczoną dokładność i są zwykle używane jako bloki konstrukcyjne do dostarczania obiektów <Rational: Fraction>, których licznikiem i mianownikiem może być dowolny typ liczby, w tym <BigInt>. Dzięki temu można przedstawiać liczby rzeczywiste, urojone i robić to z doskonałą precyzją na liczbach niewymiernych, takich jak (1/3).
Jeśli zostanie to zrobione odpowiednio <BigInt> dla arytmetyki o dużej precyzji jako standardowa funkcja JavaScript, otworzy drzwi do ogromnego zestawu operacji, w tym natywnej wydajnej kryptografii (co jest łatwe do wykonania z liczbami o dużej precyzji).
Na przykład w jednym z moich silników smalltalk z 1998 roku na procesorze 2,3 GHz, który właśnie uruchomiłem:
[10000 factorial] millisecondsToRun => 59ms 10000 factorial asString size => 35660 digits [20000 factorial] millisecondsToRun => 271ms 20000 factorial asString size => 77338 digits
Zdefiniowane jako: (ilustruje
<BigInt>
multi-precyzję w działaniu)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
Silnik V8 z pracy Larsa Baka (mojego współczesnego) wywodzi się z Animorphic Smalltalk z pracy SELF Davida Ungara wywodzącej się z Smalltalk-80, a następnie ewoluował w JVM i przerobiony przez Lars for Mobile, który pojawił się później jako podstawa silnika V8.
Wspominam o tym, ponieważ zarówno Animorphic Smalltalk, jak i QKS Smalltalk obsługują adnotacje typu, które umożliwiają silnikowi i narzędziom rozumowanie w kodzie w podobny sposób, jak TypeScript próbował dla JavaScript.
Ta podpowiedź do adnotacji i jej użycie przez język, narzędzia i mechanizmy wykonawcze oferuje możliwość obsługi wielu metod (zamiast podwójnego wysyłania) potrzebnych do prawidłowego wspierania promocji typu arytmetycznego o dużej precyzji i reguł wymuszania.
Co z kolei jest kluczem do obsługi 8/16/32/64 int / uints i wielu innych typów liczbowych w spójnej strukturze.
<Magnitude|Number|UInt64>
Przykłady wielu metod z QKS Smalltalk 1998Integer + <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.
Spodziewałbym się, że niektóre podobne wzorce zaczną się pojawiać dla obsługi JavaScript dla UIn64 / Int64 i innych typów strukturalnych lub numerycznych w miarę ewolucji <BigInt>.
źródło