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).
Uwaga: od dawna wdrażam i programuję Smalltalk, JS i inne języki oraz ich silniki i frameworki.
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 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
mantissa := (mantissa
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 :
].
^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>.