Ujemna wartość bezwzględna?…

2011-09-13 22:31

Każdemu absolwentowi szkoły podstawowej (mam nadzieję, że wciąż podstawowej…) powinna być znana poniższa funkcja:

\displaystyle |x| =  \begin{cases}   x & \text{dla } x \ge 0 \\   -x & \text{dla } x < 0 \end{cases}

Tak, jest to zwyczajna wartość bezwzględna, znana również jako moduł liczby. Jej formalna definicja gwarantuje, że |x| \ge 0 dla każdego x \in \mathbb{R}. 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:

  1. private Random random = new Random();
  2. private int identifier = -1; // niezainicjowany
  3.  
  4. private void generateIdentifier() {
  5.     identifier = Math.abs(random.nextInt());
  6. }
  7.  
  8. public boolean isInitialized() {
  9.     return identifier >= 0;
  10. }

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 :)

Tags: , ,
Author: Xion, posted under Programming »


One comment for post “Ujemna wartość bezwzględna?…”.
  1. Assa:
    September 26th, 2011 o 22:09

    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.

Comments are disabled.
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.