Jaka jest najwyższa wartość całkowita JavaScript, do której liczba może przejść bez utraty precyzji?
Czy jest to zdefiniowane przez język? Czy istnieje określone maksimum? Czy jest inaczej w różnych przeglądarkach?
21 answers
JavaScript ma dwa typy liczbowe: Number
oraz BigInt
.
Najczęściej używany typ liczby, Number
, jest 64-bitową liczbą zmiennoprzecinkową IEEE 754.
Największą dokładną wartością całkową tego typu jest Number.MAX_SAFE_INTEGER
, czyli:
- 253-1, lub
- +/- 9,007,199,254,740,991, lub
- dziewięć kwadrylionów siedem bilionów sto dziewięćdziesiąt dziewięć miliardów dwieście pięćdziesiąt cztery miliony siedemset czterdzieści tysiąc dziewięćset dziewięćdziesiąt jeden
Ujmując to w perspektywie: jeden kwadrylion bajtów to petabajt (lub tysiąc terabajtów).
"bezpieczny" w tym kontekście odnosi się do możliwości dokładnego reprezentowania liczb całkowitych i poprawnego ich porównywania.
Zauważ, że wszystkie dodatnie i ujemne liczby całkowite, których wielkość nie większe niż 253 są reprezentowalne w typie
Number
(rzeczywiście, liczba całkowita 0 ma dwa reprezentacje, +0 i -0).
Aby bezpiecznie używać liczb całkowitych większych niż ta, należy użyć BigInt
, który nie ma górnej granicy.
Zauważ, że operatory bitowe i operatory przesunięcia działają na 32-bitowych liczbach całkowitych, więc w takim przypadku maksymalna bezpieczna liczba całkowita wynosi 231-1, lub 2 147 483 647.
const log = console.log
var x = 9007199254740992
var y = -x
log(x == x + 1) // true !
log(y == y - 1) // also true !
// Arithmetic operators work, but bitwise/shifts only operate on int32:
log(x / 2) // 4503599627370496
log(x >> 1) // 0
log(x | 1) // 1
Uwaga techniczna na temat liczby 9,007,199,254,740,992: istnieje dokładna reprezentacja IEEE-754 tej wartości, oraz możesz przypisać i odczytać tę wartość ze zmiennej, więc dla bardzo starannie wybranych aplikacji w domenie liczb całkowitych mniejszych lub równych tej wartości, możesz traktować ją jako wartość maksymalną.
W ogólnym przypadku należy traktować tę wartość IEEE-754 jako niedokładną, ponieważ jest niejednoznaczne, czy koduje wartość logiczną 9,007,199,254,740,992 czy 9,007,199,254,740,993.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-22 14:37:53
>= ES6:
Number.MIN_SAFE_INTEGER;
Number.MAX_SAFE_INTEGER;
From the reference :
Number.MAX_VALUE;
Number.MIN_VALUE;
console.log('MIN_VALUE', Number.MIN_VALUE);
console.log('MAX_VALUE', Number.MAX_VALUE);
console.log('MIN_SAFE_INTEGER', Number.MIN_SAFE_INTEGER); //ES6
console.log('MAX_SAFE_INTEGER', Number.MAX_SAFE_INTEGER); //ES6
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-26 15:04:15
Jest 253 == 9 007 199 254 740 992. Dzieje się tak dlatego, że Number
S są przechowywane jako zmiennoprzecinkowe w 52-bitowej mantissie.
Wartość min wynosi -253.
To sprawia, że niektóre zabawne rzeczy się dzieją
Math.pow(2, 53) == Math.pow(2, 53) + 1
>> true
I też może być niebezpiecznie :)
var MAX_INT = Math.pow(2, 53); // 9 007 199 254 740 992
for (var i = MAX_INT; i < MAX_INT + 2; ++i) {
// infinite loop
}
czytaj dalej: http://blog.vjeux.com/2010/javascript/javascript-max_int-number-limits.html
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-15 07:10:22
W JavaScript istnieje Liczba o nazwie Infinity
.
Przykłady:
(Infinity>100)
=> true
// Also worth noting
Infinity - 1 == Infinity
=> true
Math.pow(2,1024) === Infinity
=> true
To może być wystarczające dla niektórych pytań dotyczących tego tematu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-06 22:25:27
Odpowiedź Jimmy'ego poprawnie przedstawia ciągłą liczbę całkowitą JavaScript jako -9007199254740992 na 9007199254740992 inclusive (sorry 9007199254740993, możesz myśleć, że jesteś 9007199254740993, ale mylisz się! demonstracja poniżej lub w jsfiddle).
console.log(9007199254740993);
Nie ma jednak odpowiedzi, która znalazłaby / udowadniałaby to programowo (poza tą, do której nawiązał CoolAJ86 w jego odpowiedź , która finish za 28,56 lat;), więc tutaj jest nieco bardziej efektywny sposób, aby to zrobić (dokładniej, jest bardziej wydajny o około 28,559999999968312 lat :), wraz z test fiddle:
/**
* Checks if adding/subtracting one to/from a number yields the correct result.
*
* @param number The number to test
* @return true if you can add/subtract 1, false otherwise.
*/
var canAddSubtractOneFromNumber = function(number) {
var numMinusOne = number - 1;
var numPlusOne = number + 1;
return ((number - numMinusOne) === 1) && ((number - numPlusOne) === -1);
}
//Find the highest number
var highestNumber = 3; //Start with an integer 1 or higher
//Get a number higher than the valid integer range
while (canAddSubtractOneFromNumber(highestNumber)) {
highestNumber *= 2;
}
//Find the lowest number you can't add/subtract 1 from
var numToSubtract = highestNumber / 4;
while (numToSubtract >= 1) {
while (!canAddSubtractOneFromNumber(highestNumber - numToSubtract)) {
highestNumber = highestNumber - numToSubtract;
}
numToSubtract /= 2;
}
//And there was much rejoicing. Yay.
console.log('HighestNumber = ' + highestNumber);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-14 02:37:31
To be safe
var MAX_INT = 4294967295;
Rozumowanie
Myślałem, że będę mądry i znajdę wartość, przy której x + 1 === x
z bardziej pragmatycznym podejściem.
Jeśli nie możesz czekać tak długo, jestem gotów się założyć, że
- Większość pętli nie działa przez 28,56 lat
-
9007199254740992 === Math.pow(2, 53) + 1
jest wystarczającym dowodem - powinieneś trzymać się
4294967295
czyliMath.pow(2,32) - 1
aby uniknąć oczekiwanych problemów z przesunięciem bitów
x + 1 === x
:
(function () {
"use strict";
var x = 0
, start = new Date().valueOf()
;
while (x + 1 != x) {
if (!(x % 10000000)) {
console.log(x);
}
x += 1
}
console.log(x, new Date().valueOf() - start);
}());
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-25 02:57:21
Krótka odpowiedź brzmi: "to zależy."
Jeśli używasz operatorów bitowych w dowolnym miejscu (lub jeśli odnosisz się do długości tablicy), zakresy są następujące:
Unsigned: 0…(-1>>>0)
Signed: (-(-1>>>1)-1)…(-1>>>1)
(tak się składa, że operatory bitowe i maksymalna długość tablicy są ograniczone do 32-bitowych liczb całkowitych.)
Jeśli nie używasz operatorów bitowych lub nie pracujesz z długościami tablic:
Signed: (-Math.pow(2,53))…(+Math.pow(2,53))
Ograniczenia te są narzucane przez wewnętrzna reprezentacja typu" Number", która zasadniczo odpowiada podwójnej precyzji reprezentacji zmiennoprzecinkowej IEEE 754. (Zauważ, że w przeciwieństwie do typowych podpisanych liczb całkowitych, wielkość granicy ujemnej jest taka sama jak wielkość granicy dodatniej, ze względu na cechy reprezentacji wewnętrznej, która w rzeczywistości zawiera ujemne 0!)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-17 07:13:26
ECMAScript 6:
Number.MAX_SAFE_INTEGER = Math.pow(2, 53)-1;
Number.MIN_SAFE_INTEGER = -Number.MAX_SAFE_INTEGER;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-31 05:52:35
Wiele wcześniejszych odpowiedzi pokazało, że 9007199254740992 === 9007199254740992 + 1 === true
aby zweryfikować, że 9,007,199,254,740,991 jest maksymalną i bezpieczną liczbą całkowitą.
Ale co jeśli będziemy dalej robić:
input: 9007199254740992 + 1 output: 9007199254740992 expected: 9007199254740993
input: 9007199254740992 + 2 output: 9007199254740994 expected: 9007199254740994
input: 9007199254740992 + 3 output: 9007199254740996 expected: 9007199254740995
input: 9007199254740992 + 4 output: 9007199254740996 expected: 9007199254740996
Możemy się dowiedzieć, że wśród liczb większych niż 9,007,199,254,740,992, tylko liczby parzyste są reprezentowalne.
To wstęp do wyjaśnienia, jak działa 64-bitowy format podwójnej precyzji. Zobaczmy, jak 9,007,199,254,740,992 jest reprezentowany przy użyciu tego formatu binarnego.
Po pierwsze, oto 4,503,599,627,370,496:
1 . 0000 ----- 0000 * 2^52 => 1 0000 ----- 0000.
| - 52 bits - | |exponent part| | - 52 bits - |.
Po lewej stronie strzałki mamy wartość bitową 1 , sąsiadujący punkt radix i część ułamkowa składająca się z 52 bitów . Liczba ta pomnożona przez 2^52
, która przesuwa punkt radix o 52 kroki w prawo. Punkt radix kończy się na końcu i otrzymujemy 4503599627370496 w systemie binarnym.
Kontynuujmy zwiększanie części ułamkowej o 1, aż wszystkie bity zostaną ustawione na 1, co jest równe 9,007,199,254,740,991.
1 . 0000 ----- 0000 * 2^52 => 1 0000 ----- 0000. (like above)
(+1)
1 . 0000 ----- 0001 * 2^52 => 1 0000 ----- 0001.
(+1)
1 . 0000 ----- 0010 * 2^52 => 1 0000 ----- 0010.
.
.
.
1 . 1111 ----- 1111 * 2^52 => 1 1111 ----- 1111.
Ponieważ 64-bitowy format podwójnej precyzji ściśle przydziela 52 bity dla części ułamkowej, nie ma więcej bitów, które mogłyby przenosić kolejne 1, więc możemy ustawić wszystkie bity z powrotem na 0 i manipulować częścią wykładniczą (która technicznie może iść tak wysoko, jak 2^1023
):
┏━━▶ This bit is implicit and persistent.
┃
1 . 1111 ----- 1111 * 2^52 => 1 1111 ----- 1111.
(+1) (radix point won't have anywhere to go)
1 . 0000 ----- 0000 * 2^52 * 2 => 1 0000 ----- 0000. * 2
=>
1 . 0000 ----- 0000 * 2^53 (exponent has increased)
Teraz mamy osiągnęliśmy nasz limit 9,007,199,254,740,992. Ponieważ wykładnik wzrósł do 2 do potęgi 53, a nasza część ułamkowa to tylko 52 bitów długości, format może obsługiwać tylko przyrosty 2: dla każdego przyrostu o 1 Na części ułamkowej otrzymana liczba dziesiętna jest większa o 2.
Consume 2^52 to move radix point to
the end again, you end up with:
1 . 0000 ----- 0000 * 2^53 => 1 0000 ----- 0000. * 2 <--- we're left with a multiplication of 2 here
| - 52 bits - | | - 52 bits - |.
Dlatego 64-bitowy format podwójnej precyzji Nie może zawierać liczb nieparzystych, gdy liczba jest większa niż 9,007,199,254,740,992 i dlaczego ludzie lubią zwracać uwagę, że 9007199254740992 === 9007199254740992 + 1 === true
:
Po tym wzorze, gdy liczba jest większa niż 9,007,199,254,740,992 * 2 = 18,014,398,509,481,984 tylko 4 razy ułamek może być utrzymany:
input: 18014398509481984 + 1 output: 18014398509481984 expected: 18014398509481985
input: 18014398509481984 + 2 output: 18014398509481984 expected: 18014398509481986
input: 18014398509481984 + 3 output: 18014398509481984 expected: 18014398509481987
input: 18014398509481984 + 4 output: 18014398509481988 expected: 18014398509481988
A może liczby 2,251,799,813,685,248 przez 4,503,599,627,370,495?
Widzieliśmy, że możemy technicznie wyjść poza to, co lubimy traktować jako największa możliwa liczba (jest to raczej największa bezpieczna liczba; nazywa się to MAX_SAFE_NUMBER
nie bez powodu), ale wtedy ograniczamy się do przyrostów o 2, Później 4, potem 8, itd.
Poniżej tych liczb są jednak inne ograniczenia. Ponownie, zgodnie z tym samym wzorem.
1 . 0000 ----- 0001 * 2^51 => 1 0000 ----- 000 . 1
| - 52 bits - | | - 52 bits - |
Wartość 0.1
w binarnym jest dokładnie 2^-1 (=1/2) (=0.5). Więc gdy liczba znajduje się w wyżej wymienionym zakresie, najmniej znaczący bit reprezentuje 1/2:
input: 4503599627370495.5 output: 4503599627370495.5
input: 4503599627370495.7 output: 4503599627370495.5
[[17]}większa precyzja to niemożliwe.
Mniej niż 2,251,799,813,685,248 (2^51
):
input: 2251799813685246.3 output: 2251799813685246.25
input: 2251799813685246.5 output: 2251799813685246.5
input: 2251799813685246.75 output: 2251799813685246.75
// Please note that if you try this yourself and, say, log these numbers to the console,
// they will get rounded. JavaScript rounds if the number of digits exceed 17. The value
// is internally held correctly:
input: 2251799813685246.25.toString(2)
output: "111111111111111111111111111111111111111111111111110.01"
input: 2251799813685246.75.toString(2)
output: "111111111111111111111111111111111111111111111111110.11"
input: 2251799813685246.78.toString(2)
output: "111111111111111111111111111111111111111111111111110.11"
Część wykładnicza mA 11 bitów przydzielonych jej przez format.
Z Wikipedii (po więcej szczegółów wejdź tam):
Więc aby część wykładnicza była 2^52
, dokładnie musimy ustawić e = 1075.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-20 16:22:50
Inni mogli już podać ogólną odpowiedź, ale pomyślałem, że dobrym pomysłem będzie podanie szybkiego sposobu jej określenia:
for (var x = 2; x + 1 !== x; x *= 2);
console.log(x);
Co daje mi 9007199254740992 w ciągu mniej niż milisekundy w Chrome 30.
Sprawdzi moc 2, aby znaleźć, która z nich, po dodaniu 1, równa się jemu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-19 23:14:06
Cokolwiek chcesz użyć do operacji bitowych musi być między 0x800000000 (-2147483648 lub -2^31) i 0x7fffffff (2147483647 lub 2^31 - 1).
Konsola powie Ci, że 0x80000000 równa się + 2147483648, ale 0x80000000 & 0x80000000 równa się -2147483648.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-06 22:25:53
Try:
maxInt = -1 >>> 1
W Firefoksie 3.6 jest 2^31 - 1.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-06 22:24:29
W momencie zapisu JavaScript otrzymuje nowy typ danych: BigInt
. Jest to propozycja TC39 na etapie 4, która ma zostać uwzględniona w EcmaScript 2020. BigInt
jest dostępny w Chrome 67+, FireFox 68+, Opera 54 i Node 10.4.0. Trwa w Safari, i in... Wprowadza literały numeryczne posiadające przyrostek " n " i pozwala na dowolną precyzję:
var a = 123456789012345678901012345678901n;
Precyzja nadal zostanie oczywiście utracona, gdy taka liczba zostanie (może nieumyślnie) zmuszona do danych liczbowych Typ.
I, oczywiście, zawsze będą ograniczenia precyzji ze względu na skończoną pamięć i koszt w kategoriach czasu w celu przydzielenia niezbędnej pamięci i do wykonywania arytmetyki na tak dużych liczbach.
Na przykład, generowanie liczby o stu tysiącach cyfr dziesiętnych, będzie wymagało zauważalnego opóźnienia przed zakończeniem:
console.log(BigInt("1".padEnd(100000,"0")) + 1n)
...ale to działa.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-21 10:36:08
Zrobiłem prosty test ze wzorem, X - (X+1)=-1, a największa wartość X mogę dostać się do pracy na Safari, Opera i Firefox (testowane na OS X) jest 9e15. Oto kod, którego użyłem do testowania:
javascript: alert(9e15-(9e15+1));
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-06 22:23:59
Piszę to tak:
var max_int = 0x20000000000000;
var min_int = -0x20000000000000;
(max_int + 1) === 0x20000000000000; //true
(max_int - 1) < 0x20000000000000; //true
To samo dla int32
var max_int32 = 0x80000000;
var min_int32 = -0x80000000;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-25 14:29:12
Przejdźmy do źródeł
Opis
Stała
MAX_SAFE_INTEGER
ma wartość9007199254740991
(9,007,199,254,740,991 lub ~9 kwadrylionów). Rozumowanie tej liczby jest takie, że JavaScript używa podwójnej precyzji liczb zmiennoprzecinkowych , jak określono w IEEE 754 i może bezpiecznie reprezentować tylko liczby między-(2^53 - 1)
i2^53 - 1
.Bezpieczny w tym kontekście odnosi się do możliwości dokładnego reprezentowania liczb całkowitych i poprawnego ich porównywania. Na przykład
Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2
będzie oceniać na true, co jest matematycznie niepoprawne. Patrz Liczba.isSafeInteger () aby uzyskać więcej informacji.Ponieważ
MAX_SAFE_INTEGER
jest statyczną właściwością Number , zawsze używasz jej jakoNumber.MAX_SAFE_INTEGER
, a nie jako właściwości obiektu Number, który utworzyłeś.
Zgodność przeglądarki
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55
W Google Chrome wbudowany javascript, można przejść do około 2^1024 zanim liczba nazywa się nieskończoność.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-10 01:05:22
W JavaScript reprezentacja liczb to 2^53 - 1
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-19 21:47:10
Scato wrotes:
Wszystko, czego chcesz użyć do operacji bitowych, musi być pomiędzy 0x80000000 (-2147483648 lub -2^31) i 0x7fffffff (2147483647 lub 2^31 - 1).
Konsola powie Ci, że 0x80000000 równa się + 2147483648, ale 0x80000000 & 0x80000000 równa się -2147483648
Hex-Decimals są niepodpisanymi dodatnimi wartościami, więc 0x80000000 = 2147483648-to matematycznie poprawne. Jeśli chcesz, aby była to wartość podpisana, musisz prawy shift: 0x80000000 > > 0 = -2147483648. Możesz zamiast tego napisać 1
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-24 18:34:55
Firefox 3 chyba nie ma problemu z wielkimi liczbami.
1E+200 * 1E+100 obliczy grzywnę do 1E+300.
Safari też nie ma z tym problemu. (Dla przypomnienia, jest to na komputerze Mac, jeśli ktoś inny zdecyduje się to przetestować.)
O ile nie straciłem mózgu o tej porze dnia, to jest o wiele większe niż 64-bitowa liczba całkowita.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-06 22:23:03
Węzeł.wydaje się, że js i Google Chrome używają 1024-bitowych wartości zmiennoprzecinkowych, więc: {]}
Number.MAX_VALUE = 1.7976931348623157e+308
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-06 22:26:22