Does JavaScript support 64-bit integers?
Solution 1
JavaScript represents numbers using IEEE-754 double-precision (64 bit) format. As I understand it this gives you 53 bits precision, or fifteen to sixteen decimal digits. Your number has more digits than JavaScript can cope with, so you end up with an approximation.
This isn't really "mishandling" as such, but obviously it isn't very helpful if you need full precision on large numbers. There are a few JS libraries around that can handle larger numbers, e.g., BigNumber and Int64.
Solution 2
Chromium version 57 and later natively supports arbitrary-precision integers. This is called BigInt and is being worked on for other browsers as well. It is dramatically faster than JavaScript implementations.
Solution 3
I.e., V8 JavaScript is a Smalltalk derived engine. (1980s - present) Lisp and Smalltalk engines support multi-precision arithmetic using <LargeInteger> sometimes called <BigInt>. Spoiler, the Dart team at Google is largely a bunch of ex-Smalltalkers bringing their experience together into the JS space.
These types of numbers have unlimited precision and are typically used as building blocks to provide <Rational:Fraction> objects whose numerator and denominator can be any type of number including a <BigInt>. With that one can represent real-numbers, imaginary-numbers, and do so with perfect precision on irrational numbers like (1/3).
Note: I'm a long time implementer and developer of Smalltalk, JS and other languages and their engines and frameworks.
If done appropriately <BigInt> for multi-precision arithmetic as a standard feature of JavaScript will open the door to a tremendous suite of operations, including native efficient cryptography (which is easy to do with multi-precision numbers).
For example, in one of my 1998 smalltalk engines, on a 2.3GHz cpu I just ran:
[10000 factorial] millisecondsToRun => 59ms
10000 factorial asString size => 35660 digits
[20000 factorial] millisecondsToRun => 271ms
20000 factorial asString size => 77338 digits
Defined as: (illustrating <BigInt>
multi-precision in action)
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
The V8 engine from Lars Bak (a contemporary of mine) work is derived from Animorphic Smalltalk from David Ungar's SELF work derived from Smalltalk-80, and subsequently evolved into the JVM, and redone by Lars for Mobile emerging later as the V8 engine foundation.
I mention that because both Animorphic Smalltalk and QKS Smalltalk support type-annotations which enable the engine and tools to reason about code in a similar way to that which TypeScript has attempted for JavaScript.
That annotation hinting and its use by the language, tools, and runtime engines offers the capability to support multi-methods (rather than double dispatch) needed to support multi-precision arithmetic type-promotion and coercion rules properly.
Which, in turn, is key to supporting 8/16/32/64 int/uints and many other numeric types in a coherent framework.
Multi-method <Magnitude|Number|UInt64>
examples from 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.
I would expect that some similar patterns will begin to emerge for JavaScript support of UIn64/Int64 and other structural or numeric types as <BigInt> evolves.
ahmd0
Updated on July 08, 2022Comments
-
ahmd0 almost 2 years
I have the following code:
var str = "0x4000000000000000"; //4611686018427387904 decimal var val = parseInt(str); alert(val);
I get this value: "
4611686018427388000
", which is0x4000000000000060
I was wondering if JavaScript is mishandling 64-bit integers or am I doing something wrong?
-
nnnnnn about 12 yearsI think I was editing one solution into my answer as you were typing your comment: you can use a library that will handle it for you. (I've never needed to handle such big numbers in JS except for occasional use as record IDs in which case I didn't need to do maths operations on them so keeping them as strings worked fine.)
-
Jeremy Condit about 11 yearsClosure's goog.math.Long may also be helpful: docs.closure-library.googlecode.com/git/…
-
sellibitze about 10 yearsOne should maybe add that bit-level operations are limited to 32 bit IIUC.
-
benizi almost 9 yearsgoog.math.Long documentation has moved: google.github.io/closure-library/api/class_goog_math_Long.html
-
falsarella over 8 years(Comment by @Michaelangelo) Unfortunately, the ECMAScript 2015 Specifications (version 6) has no official support for
UInt64
; while Mozilla has added support for UInt64 -- this is non-standard. WebGL has similar needs but unfortunately there is noUint64Array
either, only Uint32Array. -
benizi almost 7 yearsgoog.math.Long documentation has moved again: google.github.io/closure-library/api/goog.math.Long.html (Thanks, @Pacerier)
-
trusktr over 6 years"WebGL has similar needs but unfortunately there is no Uint64Array either, only Uint32Array." But if you want to ship a custom game for example, you could probably fork Chrome and add that. Does anyone know of anyone doing stuff like this?
-
Peter Zhao about 6 yearsA Long class for representing a 64 bit two's-complement integer value derived from the Closure Library for stand-alone use and extended with unsigned support. github.com/dcodeIO/long.js
-
bryc about 6 yearsAlso, there is massive performance (85~95%) loss when using a library for 64-bit arithmetic. This is a real drag, because 32-bit arithmetic is much more efficient (especially when
Math.imul
is relevant). -
oisyn over 5 yearsRe "This isn't really "mishandling" as such": I would call this particular issue mishandling though, as a double is perfectly capable of representing 0x4000000000000000 as that's a power of two and well within double's exponent range.
-
christo8989 about 5 yearsBtw, I think ES10 or something will have Big ints.
-
David Callanan about 5 yearsAlso supported by Opera 54+ and Node.js. Firefox 65+ supports it if
javascript.options.bigint
flag is enabled. -
Cibo FATA8 over 4 yearsIt is not always faster. compare this
console.time("go");for (var i=0;i<10000000;++i) {} console.timeEnd("go");
vs 64bit numbersconsole.time("go");for (var i=0n;i<10000000n;++i) {} console.timeEnd("go");
-
hippietrail about 4 years@CiboFATA8: He is talking about BigInt as a native browser component vs BigInt implemented in JavaScript. You are comparing js Numbers, which are floats with about 53 bits of precision (not 64) with native browser BigInt (also not 64 bit).
-
Raj Pawan Gumdal about 3 years@sellibitze - I was looking at this post coz of bit level operation. What should one do if we need around 50+ bits to store different bitwise information?
-
Admin almost 3 yearsUpvote for the history bit, is there some paper about it?
-
Javier Rey over 2 yearsIt's actually 54-bit precision, one bit for the sign, so the range is -2^53 to +2^53 (minus 1)
-
SilverbackNet over 2 yearsFWIW, BigInt and its many methods is now standard in ES 11, and all major browsers.