Liczenie miesięcy przerosło kalkulator z Windowsa. Pomoc przyszła z zewnątrz

Strona główna Aktualności
Liczenie miesięcy przerosło kalkulator z Windowsa. Pomoc przyszła z zewnątrz
Liczenie miesięcy przerosło kalkulator z Windowsa. Pomoc przyszła z zewnątrz

O autorze

Na pewno znasz to uczucie. Przeglądasz forum lub Reddita, znajdujesz opis błędu i zastanawiasz się jak go naprawić. To samo przytrafiło się niemieckiemu programiście, który naprawił interesujący błąd w kalkulatorze systemu Windows.

Peter dokładnie opisał swoją przygodę. Przeglądając Reddita, trafił na opis błędu, który występuje przy odejmowaniu dat w programie Calc.exe. To jeden z tych zabawnych błędów, którymi natychmiast chce się pochwalić w internecie. Użytkownik Reddita chciał policzyć, ile czasu jest między 31 lipca i 31 grudnia. Kalkulator Microsoftu podał wynik: 5 miesięcy, 613566756 tygodni i 3 dni.

Błąd można bez trudu odtworzyć – możesz spróbować. Na komputerze Petera pojawiał się przy liczeniu czasu do 30 grudnia. Duża liczba tygodni wygląda jakby gdzieś „przekręcił się licznik” albo autorzy popełnili klasyczny „off by one”. Peter zaciekawiony problemem przystąpił do badania sprawy. Jako że kod programu Calc .exe jest otwarty, mógł od razu zasiąść do pracy w Visual Studio.

Liczenie dat jest trudniejsze, niż myślisz

Analizując plik DateCalculator.cpp programista szybko odkrył, że nie jest to problem z przepełnieniem ani konwersją zmiennych. Na pierwszy rzut oka wszystko było w porządku. Kalkulator zaczyna liczyć różnicę od lat, potem liczy miesiące, potem pozostałe dni. Błąd występuje na poziomie obliczeń.

Z używanym przez nas obecnie kalendarzem jest mnóstwo problemów. Jeśli dodasz do 31 lipca miesiąc, otrzymasz 31 sierpnia. Jeśli dodasz dwa – 30 września. Po dodaniu czterech będzie 30 listopada, a pięciu – 31 grudnia. Przestrzeń liczbowa dni w roku jest nieregularna, a do tego mamy luty z dniem przestępnym.

Liczba całkowita ze znakiem i bez znaku

Skąd bierze się tak duża liczba? Pewnie się już domyślasz, że jest wynikiem „przekręconego licznika” w zmiennej typu unsigned int (liczba całkowita bez znaku). Każdy układ bitów w tej zmiennej jest liczbą naturalną. Odjęcie 1 od zera daje jako wynik bardzo dużą wartość dodatnią. Konwersja na liczbę całkowitą ze znakiem zaś zamienia najstarszy bit na znak, pozostałe na wartość.

W obliczeniach różnicy w zmiennej typu unsigned int zapisywana jest wartość ujemna. Ponieważ ten typ nie ma znaku, do dalszych obliczeń na poziomie tygodni trafia bardzo duża wartość dodatnia, ponownie powiększana w kolejnej pętli. To ona powoduje wyświetlanie absurdalnych wyników w kalkulatorze.

Peter wysłał już swoje rozwiązanie problemu na GitHub i zgłosił Pull Request do repozytorium Microsoftu. Jego rozwiązanie załatwia tylko część problemów. Spójrz na ten wynik:

Na poziomie liczbowym się zgadza, ale nie jest to odpowiedź, której oczekiwałby człowiek. Zobaczymy, co z tym zrobi Microsoft.

© dobreprogramy