JavaScript 是不是支持 64 位整数?
Posted
技术标签:
【中文标题】JavaScript 是不是支持 64 位整数?【英文标题】:Does JavaScript support 64-bit integers?JavaScript 是否支持 64 位整数? 【发布时间】:2012-03-27 11:15:00 【问题描述】:我有以下代码:
var str = "0x4000000000000000"; //4611686018427387904 decimal
var val = parseInt(str);
alert(val);
我得到这个值:“4611686018427388000
”,即0x4000000000000060
我想知道 javascript 是否错误地处理了 64 位整数,还是我做错了什么?
【问题讨论】:
相关:What is JavaScript's Max Int? What's the highest Integer value a Number can go to without losing precision? What is JavaScript's highest integer value that a number can go to without losing precision?的可能重复 【参考方案1】:也就是说,V8 JavaScript 是 Smalltalk 派生的引擎。 (1980 年代至今)Lisp 和 Smalltalk 引擎支持使用
这些类型的数字具有无限精度,通常用作构建块来提供
注意:我是 Smalltalk、JS 和其他语言及其引擎和框架的长期实施者和开发者。
如果将多精度算术的
例如,在我 1998 年的一个 smalltalk 引擎中,我刚刚在 2.3GHz cpu 上运行:
[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
来自 Lars Bak(我的同时代人)工作的 V8 引擎源自 David Ungar 的源自 Smalltalk-80 的 SELF 工作的 Animorphic Smalltalk,随后演变为 JVM,并由 Lars for Mobile 重做,后来成为 V8发动机基础。
我提到这一点是因为 Animorphic Smalltalk 和 QKS Smalltalk 都支持类型注释,这使引擎和工具能够以类似于 TypeScript 为 JavaScript 尝试的方式推理代码。
注释提示及其在语言、工具和运行时引擎中的使用提供了支持正确支持多精度算术类型提升和强制规则所需的多方法(而不是双重调度)的能力。
这反过来又是在一个连贯的框架中支持 8/16/32/64 int/uint 和许多其他数字类型的关键。
来自 QKS Smalltalk 1998 的多方法 <Magnitude|Number|UInt64>
示例
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.
我希望随着
【讨论】:
赞成历史位,有没有关于它的论文?【参考方案2】:Chromium 57 及更高版本本机支持任意精度整数。这称为 BigInt,对于其他浏览器也是 being worked on。它比 JavaScript 实现是 dramatically faster。
【讨论】:
Opera 54+ 和 Node.js 也支持。如果启用了javascript.options.bigint
标志,Firefox 65+ 支持它。
它并不总是更快。比较这个 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 作为原生浏览器组件与在 JavaScript 中实现的 BigInt。您正在比较 js 数字,它是具有大约 53 位精度(不是 64 位)的浮点数与本机浏览器 BigInt(也不是 64 位)。【参考方案3】:
JavaScript 使用IEEE-754 双精度(64 位)格式表示数字。据我了解,这为您提供了 53 位精度,或 15 到 16 个十进制数字。你的数字比 JavaScript 处理的数字多,所以你最终得到一个近似值。
这并不是真正的“错误处理”,但如果您需要对大数进行完全精确处理,显然它不是很有帮助。周围有一些 JS 库可以处理更大的数字,例如 BigNumber 和 Int64。
【讨论】:
Closure 的 goog.math.Long 也可能有帮助:docs.closure-library.googlecode.com/git/… 也许应该补充一点,位级操作仅限于 32 位 IIUC。 goog.math.Long 文档已移动:google.github.io/closure-library/api/class_goog_math_Long.html (@Michaelangelo评论)不幸的是,ECMAScript 2015 Specifications(第 6 版)没有对UInt64
的官方支持;虽然 Mozilla 增加了对UInt64 的支持——这是非标准的。 WebGL 也有类似的需求,可惜也没有Uint64Array
,只有Uint32Array。
goog.math.Long 文档再次移动:google.github.io/closure-library/api/goog.math.Long.html(感谢@Pacerier)以上是关于JavaScript 是不是支持 64 位整数?的主要内容,如果未能解决你的问题,请参考以下文章