Gdzie się kończą granice, a jednak nadal mają swój ciąg - tam zaczyna się nieskończoność!

Ale żeby dokładniej wiedzieć, z czym mamy do czynienia, użyjmy operatora typeof, aby dowiedzieć się z jakim typem danych mamy do czynienia!

typeof Infinity // "number"

Przyjrzyjmy się pierwszemu przypadkowi Infinity

1 / 0 //Infinity
1 / -0 //-Infinity

Dlaczego tak się dzieje?

To wynika z matematycznej koncepcji granic:

$$\lim_{x \to 0^+} \frac{1}{x} = +\infty$$$$\lim_{x \to 0^-} \frac{1}{x} = -\infty$$

Gdy dzielimy przez liczbę coraz bliższą zeru od strony dodatniej ($0^+$), wynik rośnie do $+\infty$.

Gdy dzielimy przez liczbę coraz bliższą zeru od strony ujemnej ($0^-$), wynik maleje do $-\infty$.

JavaScript rozróżnia +0 i -0, dlatego 1/0 daje Infinity, a 1/-0 daje -Infinity.

Jak widać - nie trzeba było się zbytnio natrudzić, aby zobaczyć ten przypadek w akcji.

Spróbujmy jej zatem poszukać :)

let value = 1;

while (value !== Infinity) {
  let next = value * 2;

  if (next === Infinity) {
    console.log('Ostatnia wartość:', value.toExponential());
    console.log('Następna wartość:', next);
    break;
  }

  value = next;
}

Output:

Ostatnia wartość: 8.98846567431158e+307
Następna wartość: Infinity

Jak widać - znalezienie nieskończoności nie jest takie trudne - JavaScript nam to znacznie ułatwia podając na tacy wartość maksymalną przed wkroczeniem w Infinity zamiast żmudnego i niewydajnego iterowania:

Number.MAX_VALUE

Output:

1.7976931348623157e+308

Co 1.7976931348623157e+308 przed nieskończonością (Infinity) reprezentuje?

179,769,313,486,231,570,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,
000,000,000,000,000

To niewyobrażalnie wielka liczba, większa niż liczba atomów we wszechświecie $\sim 10^{80}$ czy liczba możliwych partii szachowych $\sim 10^{120}$.

Dlaczego zatem taka wartość? W czasach definiowania standardu w 1985 roku pamięć RAM była droga, a wybór 64 bitów dla double to świadoma decyzja projektowa. Ta wartość wystarczy do obliczeń inżynierskich, większości symulacji fizycznych, astronomii i w finansach.

Tak więc to był wystarczający kompromis dla dobrej precyzji (15-17 cyfr), efektywnej dla sprzętu i uniwersalnej (wspieranej przez każdy procesor).

Standard IEEE 754 jako regulator Infinity

W standardzie IEEE 754 zostały zdefiniowane zarówno NaN, jak i Infinity, więc to nie jest tylko feature JavaScript, ale również innych języków programowania!

#include <iostream>
#include <limits>
 
int main()
{
    double max = std::numeric_limits<double>::max();
    double inf = std::numeric_limits<double>::infinity();
 
    if (inf > max)
        std::cout << inf << " is greater than " << max << '\n';
}

Output:

inf is greater than 1.79769e+308

Dlaczego zamiast liczby został wypisany inf?

Infinity to wartość, którą da się przedstawić bitowo, ale która nie reprezentuje żadnej konkretnej wartości liczbowej - zamiast tego reprezentuje koncepcję matematycznej nieskończoności. Zatem jeśli ktoś by chciał wypisać surowe bity to w odpowiedzi otrzymałby

+Infinity = 0x7FF0000000000000
-Infinity = 0xFFF0000000000000

inf jest po prostu czytelniejsze i wygodniejsze od reprezentacji bitowej, tak samo jak Infinity w JavaScript.

Po co więc ten koncept został wprowadzony w standardzie IEEE 754?

Zamiast przerywać działanie programu przy dzieleniu przez zero lub overflow, IEEE 754 daje wartość, z którą można dalej pracować:

const result = calculate_something();

if (result === Infinity) {
   console.error("Wartość za duża!");
   return Number.MAX_VALUE; 
}

Bez Infinity - crash lub losowe zachowanie.

Również dzięki Infinity zachowujemy matematyczną poprawność

1 / Infinity === 0           // lim(1/x) gdy x→∞
Infinity + 5 === Infinity    // ∞ + c = ∞
Infinity * Infinity          // ∞ × ∞ = ∞

Ale uwaga - niektóre operacje z Infinity dają NaN (nieokreślone):

Infinity - Infinity  // NaN (∞ - ∞ jest nieokreślone)
Infinity * 0         // NaN (∞ × 0 jest nieokreślone)
Infinity / Infinity  // NaN (∞ / ∞ jest nieokreślone)

Przykładowe zastosowania

Wyłączenie timeout:

const config = {
    timeout: debugMode ? Infinity : 5000  // brak timeout w debug
};

setTimeout(() => {
    console.log('To się nigdy nie wykona');
}, config.timeout);

Uwaga: Niektóre przeglądarki limitują timeout do 32-bit int (~24 dni max). Infinity może zostać przekonwertowane do tej wartości.

Sortowanie:

const items = [
    { name: 'Alice', priority: 1 },
    { name: 'Bob', priority: 2 },
    { name: 'System', priority: Infinity }  // Zawsze na końcu
];

items.sort((a, b) => a.priority - b.priority);

Links: