Każdemu absolwentowi szkoły podstawowej (mam nadzieję, że wciąż podstawowej…) powinna być znana poniższa funkcja:
Tak, jest to zwyczajna wartość bezwzględna, znana również jako moduł liczby. Jej formalna definicja gwarantuje, że dla każdego
. Innymi słowy, wartość bezwzględna nigdy nie może być ujemna. Nic więc dziwnego, że używamy jej chętnie w tych właśnie sytuacjach, gdy interesuje nas tylko dodatnia połowa osi liczb. Oto bardzo prosty przykład:
Mamy tu elementarną sztuczkę, polegającą na początkowym przypisaniu polu identifier
wartości spoza zwykłej dziedziny (-1
), co oznaczać ma brak identyfikatora. Możemy tak zrobić, bo dzięki wartości bezwzględnej (funkcji Math.abs
) generowane losowe identyfikatory będą zawsze dodatnie.
Albo raczej prawie zawsze…
Owo “prawie” oznacza jedną konkretną wartość liczbową, dla której tak nie będzie. Wartość ta jest ujemna. I jeśli będziemy mieli pecha na nią trafić, funkcja abs
pozostawi ją ujemną. Dlaczego? Ano dlatego, że liczba przeciwna do niej nie mieści się w danym typie liczbowym!
Chodzi tutaj o najmniejszą liczbę ujemną: -231 w przypadku typu 32-bitowego ze znakiem. W Javie odpowiada ona stałej Integer.MIN_VALUE
. Przeciwna do niej liczba 231 jest zbyt duża, żeby mogła się zmieścić w typie int
– jest bowiem dokładnie o 1 większa niż Integer.MAX_VALUE
.
Istnieje zatem liczba typu int
, której negacja nie jest liczbą typu int
. Skąd się wzięła ta asymetria? Otóż “winne” jest zero, które też jest jedną z możliwych wartości do zapisania w zmiennej 32-bitowej. Cały zakres od -231 do 231 zawiera więc 232+1 wartości – o jedną za dużo. Dlatego też dodatni koniec przedziału nie należy do zbioru wartości typu int
.
A czemu nie ujemny?… To już jest spowodowane sposobem zapisywania liczb ze znakiem w postaci binarnej.
Oczywiście szansa na losowe trafienie akurat w Integer.MIN_VALUE
nie jest duża. Jeśli generujemy liczbę raz na jedną milisekundę, to oczekiwany czas do wystąpienia problemu to 49… dni. Być może więc nigdy na niego nie natrafimy – no, może co najwyżej w postaci ostrzeżenia od statycznego analizatora kodu.
Skoro już jednak wiemy o problemie, to może warto poświęcić chwilkę (np. 2-31 czasu potrzebnego na poprawienie błędu o częstości 100%), żeby go rozwiązać? Można w tym celu zastosować chociażby jedną z poniższych modyfikacji losowania:
while (identifier < 0)
identifier = Math.abs(random.nextInt());
// ...
identifier = random.nextInt(Integer.MAX_VALUE);
// ...
final int MAX_IDENTIFIER = 65536; // albo coś w tym stylu
identifier = random.nextInt(MAX_IDENTIFIER);[/java]
Jeśli nie ma żadnych specyficznych przeciwwskazań, to osobiście preferowałbym tę ostatnią.
Przy okazji składam wszystkim życzenia z okazji Dnia Programisty. Niech wszystkie problemy w kodzie będą tak łatwe do poprawienia, jak ten :)
Muszę cię zasmucić: o wartości bezwzględnej mówiliśmy dopiero niedawno w 2 klasie Gim.
Podoba mi się to w jaki sposób opisujesz pewne problem aczkolwiek, to chyba wie każdy.