JavaScriptは64ビット整数をサポートしていますか?


81

私は次のコードを持っています:

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

私はこの値を取得します: " 4611686018427388000"、これは0x4000000000000060

JavaScriptが64ビット整数を誤って処理しているのか、それとも何か間違っているのか疑問に思っていました。



回答:


88

JavaScriptは、IEEE-754倍精度(64ビット)形式を使用して数値を表します。私が理解しているように、これにより53ビットの精度、つまり15〜16桁の10進数が得られます。あなたの数はJavaScriptが処理できるよりも多くの桁を持っているので、あなたは概算になってしまいます。

これ自体は実際には「誤った取り扱い」ではありませんが、大きな数に対して完全な精度が必要な場合は、明らかにあまり役に立ちません。BigNumberInt64など、より大きな数を処理できるJSライブラリがいくつかあります。


2
Closureのgoog.math.Longも役立つ場合があります:docs.closure-library.googlecode.com/git/…–
Jeremy Condit

39
ビットレベルの演算は32ビットIIUCに制限されていることを付け加えておく必要があります。
Sellibitze 2014年

2
goog.math.Longドキュメントが移動しました:google.github.io/closure-library/api/class_goog_math_Long.html
benizi

5
@Michaelangeloによるコメント)残念ながら、ECMAScript 2015仕様(バージョン6)はUInt64;を公式にサポートしていません。MozillaはUInt64のサポートを追加しましたが、これは非標準です。WebGLにも同様のニーズがありますが、残念ながらUint64Arrayどちらもありません。Uint32Arrayのみです
falsarella 2015

2
goog.math.Longドキュメントが再び移動しました:google.github.io/closure-library/api/goog.math.Long.html(Thanks、@ Pacerier)
benizi 2017

11

Chromiumバージョン57以降は、任意精度の整数をネイティブにサポートします。これはBigIntと呼ば、他のブラウザでも作業中です。それは劇的に速くJavaScriptの実装より。


Opera54 +およびNode.jsでもサポートされています。Firefox 65+は、javascript.options.bigintフラグが有効になっている場合にそれをサポートします。
DavidCallanan19年

常に速いとは限りません。これ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");
CiboFATA819年

@ CiboFATA8:彼は、ネイティブブラウザコンポーネントとしてのBigIntとJavaScriptで実装されたBigIntについて話しています。約53ビットの精度(64ビットではない)のfloatであるjs Numbersを、ネイティブブラウザーのBigInt(64ビットではない)と比較しています。
ヒッピー・トレイル

0

つまり、V8JavaScriptはSmalltalkから派生したエンジンです。(1980年代-現在)LispおよびSmalltalkエンジンは、<BigInt>と呼ばれることもある<LargeInteger>を使用した多倍長演算をサポートします。ネタバレ、ダーツグーグルチームは主に元ス​​モールトーカーの集まりであり、彼らの経験をJSスペースにまとめています。

これらのタイプの数値は無制限の精度を持ち、通常、分子と分母が<BigInt>を含む任意のタイプの数値である<Rational:Fraction>オブジェクトを提供するためのビルディングブロックとして使用されます。これにより、実数、虚数を表すことができ、(1/3)のような無理数に対して完全な精度で表すことができます。

注:私は、Smalltalk、JS、その他の言語、およびそれらのエンジンとフレームワークの長年の実装者および開発者です。

JavaScriptの標準機能として多倍長演算用に適切に<BigInt>を実行すると、ネイティブの効率的な暗号化(多倍長数で簡単に実行可能)を含む、膨大な一連の操作への扉が開かれます。

たとえば、1998年のsmalltalkエンジンの1つで、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エンジンは、Smalltalk-80から派生したDavidUngarのSELF作業からのAnimorphicSmalltalkから派生し、その後JVMに進化し、後にV8エンジンの基盤として登場したLars forMobileによって再実行されました。

AnimorphicSmalltalkとQKSSmalltalkはどちらも、TypeScriptがJavaScriptに対して試みたのと同様の方法で、エンジンとツールがコードについて推論できるようにする型注釈をサポートしているためです。

その注釈のヒントと、言語、ツール、およびランタイムエンジンによるその使用により、多倍長算術型の昇格および強制ルールを適切にサポートするために必要なマルチメソッド(ダブルディスパッチではなく)をサポートする機能が提供されます。

これは、コヒーレントフレームワークで8/16/32/64 int / uintsおよび他の多くの数値型をサポートするための鍵となります。

<Magnitude|Number|UInt64>QKS Smalltalk1998のマルチメソッドの例

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.

<BigInt>が進化するにつれて、UIn64 / Int64およびその他の構造型または数値型のJavaScriptサポート用にいくつかの同様のパターンが出現し始めると思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.