Czy komputer spróbuje podzielić przez zero?

59

Wszyscy wiemy, że 0/0jest Undefinedi zwraca błąd, gdybym włożył go do kalkulatora, a gdybym stworzył program (przynajmniej w C), system operacyjny zakończyłby go, gdy spróbuję podzielić przez zero.

Zastanawiam się jednak, czy komputer nawet próbuje podzielić przez zero , czy też po prostu ma „wbudowaną ochronę”, więc kiedy „widzi” 0/0, zwraca błąd nawet przed próbą jego obliczenia?

Ankush
źródło
10
0/0 jest niezdefiniowane, każda inna liczba / 0 to inny rodzaj błędu, wydajesz się mylić dwa
edc65
7
Każda liczba podzielona przez 0 jest niezdefiniowana matematycznie.
ManoDestra
12
@jwg: „gdyby miał wartość, wyniósłby 1” - niekoniecznie; istnieją całe gałęzie matematyki poświęcone wartości, jaka może być w różnych okolicznościach :)
psmears
11
Aby wyjaśnić tutaj terminologię, 0/0 nazywa się nieokreśloną formą, podczas gdy x / 0 dla niezerowej x jest niezdefiniowane . Obliczenie, które kończy się na 0/0, często może być obliczone w inny sposób, aby dać prawdziwą odpowiedź, podczas gdy x / 0 jest w zasadzie bez znaczenia.
Era
9
@ jwg, być może zainteresuje Cię zasada l'hopital. Zdecydowanie są przypadki, w których 0/0 nie implikuje wartości 1.
d0nut

Odpowiedzi:

74

Procesor ma wbudowane wykrywanie. Większość architektur zestawu instrukcji określa, że ​​procesor będzie przechwytywał procedurę obsługi wyjątków dla dzielenia liczb całkowitych przez zero (nie sądzę, że to obchodzi, czy dywidenda wynosi zero).

Możliwe jest, że sprawdzenie zerowego dzielnika odbywa się równolegle sprzętowo wraz z próbą wykonania podziału, jednak wykrycie warunku skutecznego anuluje podział i pułapki, więc nie możemy tak naprawdę stwierdzić, czy jakaś część z tego podjęto próbę podziału, czy nie.

(Sprzęt często działa w ten sposób, wykonując wiele czynności równolegle, a następnie wybierając odpowiedni wynik, ponieważ wtedy każda z operacji może rozpocząć się od razu, zamiast serializować wybór odpowiedniej operacji).

Ten sam mechanizm pułapki na wyjątek będzie również używany, gdy włączone jest wykrywanie przepełnienia, o które zwykle prosisz, używając różnych instrukcji add / sub / mul (lub flagi na tych instrukcjach).

Dzielenie zmiennoprzecinkowe ma również wbudowane wykrywanie dzielenia przez zero, ale zwraca inną wartość ( IEEE 754 określa NaN ) zamiast wychwytywania do procedury obsługi wyjątku.


Hipotetycznie rzecz biorąc, jeśli procesor pominąłby jakiekolwiek wykrycie przy próbie podzielenia przez zero, problemy mogą obejmować:

  • zawieszenie procesora (np. w pętli inf.) - może się tak zdarzyć, jeśli procesor używa algorytmu do dzielenia, który zatrzymuje się, gdy licznik jest mniejszy niż dzielnik (w wartości bezwzględnej). Takie zawieszenie prawie by się liczyło jako awaria procesora.
  • (możliwą do przewidzenia) odpowiedź na śmiecie, jeśli CPU używa licznika do zakończenia dzielenia przy maksymalnej możliwej liczbie kroków dzielenia (np. 31 lub 32 na maszynie 32-bitowej).
Erik Eidt
źródło
51
@Ankush „Awaria systemu” tak naprawdę nie dzieje się na poziomie procesora. Najbardziej prawdopodobne zachowanie procesora, który nie pułapkował przy dzieleniu przez zero, polegałoby na tym, że po prostu wykonuje operację dzielenia, generuje jakiś nonsens i kontynuuje działanie. Podobnie jak po dodaniu dwóch liczb całkowitych, które się przelały.
Ixrec
6
W przypadku liczb zmiennoprzecinkowych wybór między „NaN” a „pułapką” można zwykle ustawić, odwracając flagi w FPU.
Vatine
10
@Ankush W przypadku, gdy to, co powiedział lxrec, było niejasne: jeśli chodzi o procesor, nie ma czegoś takiego jak awaria.
user253751
7
@Ankush Niewiele sytuacji powoduje „awarię systemu” na poziomie procesora. W takich przypadkach mówimy o takich sprawach, jak wyłączenie zabezpieczenia termicznego (ochrona przed przegrzaniem), potrójne awarie i podobne sytuacje. Prawie każda awaria, którą napotkasz podczas zwykłego użytkowania, w tym nieprawidłowe kody , jest obsługiwana przez wychwytywanie i odzyskiwanie w jakiś sposób, lub po prostu ignorowanie błędu i kontynuowanie wykonywania w potencjalnie nieczystym stanie, może po ustawieniu flagi błędu.
CVn
8
Jeśli nie pułapujesz dzielenia przez zero, w programowaniu potocznym wynikiem byłoby „Niezdefiniowane zachowanie”. Oznacza to, że komputer może zrobić wszystko. Może, jak wspomina Bergi, wejść w pętlę buggy i się zawiesić. Może po prostu wyprowadzać pewną nieokreśloną serię bitów, która przypadła na ich implementację logiki podziału (pamiętaj, że komputer nie „dzieli” w sensie matematycznym. Wykonuje operację na dwóch liczbach, co jest wystarczająco bliskie podziałowi że zazwyczaj nazywamy to po prostu „dywizją” (patrz także zaokrąglenie zmiennoprzecinkowe).
Cort Ammon
34

Zależy to od języka, od kompilatora, od tego, czy używasz liczb całkowitych czy liczb zmiennoprzecinkowych i tak dalej.

W przypadku liczby zmiennoprzecinkowej większość implementacji używa standardu IEEE 754 , w którym podział przez 0 jest dobrze zdefiniowany. 0/0 daje dobrze zdefiniowany wynik NaN (nie-liczba), a x / 0 dla x ≠ 0 daje + Infinity lub -Infinity, w zależności od znaku x.

W językach takich jak C, C ++ itp. Dzielenie przez zero wywołuje niezdefiniowane zachowanie. Zgodnie z definicją języka wszystko może się zdarzyć. Zwłaszcza rzeczy, których nie chcesz się wydarzyć. Jak wszystko, co działa idealnie dobrze, gdy piszesz kod i niszczy dane, gdy klient go używa. Z punktu widzenia języka nie rób tego . Niektóre języki gwarantują, że aplikacja się zawiesi; jak to zostanie wdrożone, zależy od nich. W przypadku tych języków podział przez zero ulegnie awarii.

Wiele procesorów ma wbudowaną instrukcję „dzielenia”, która zachowuje się inaczej w zależności od procesora. W 32-bitowych i 64-bitowych procesorach Intel instrukcje „dzielenia” powodują awarię aplikacji podczas próby dzielenia przez zero. Inne procesory mogą zachowywać się inaczej.

Jeśli kompilator wykryje, że wystąpi dzielenie przez zero, gdy wykonasz jakiś kod, a kompilator jest miły dla użytkowników, prawdopodobnie wyświetli ostrzeżenie i wygeneruje wbudowaną instrukcję „dzielenia”, aby zachowanie było podobnie.

gnasher729
źródło
22
„W 32-bitowych i 64-bitowych procesorach Intel instrukcje„ dzielenia ”powodują awarię aplikacji podczas próby dzielenia przez zero”. Wymagany cytat. Procesory nie mają pojęcia o aplikacjach, wykonują instrukcje i (jeśli uwzględniamy MMU) wymuszają ograniczenia dostępu do pamięci (chyba że w pierścieniu 0 lub równoważne w architekturach innych niż Intel-x86). To, że instrukcja jest częścią aplikacji A, a nie aplikacji B lub składnika systemu operacyjnego, nie ma znaczenia dla procesora; istotne jest, czy instrukcja może być instrukcją X, czy użyć adresu pamięci Y.
CVn
1
Aby dodać do komentarza @ MichaelKjörling: System operacyjny ma sposoby powiadamiania o zastosowaniu tego (i innych rodzajów błędów). W świecie Windows jest to EXCEPTION_INT_DIVIDE_BY_ZEROwartość w EXCEPTION_RECORDktóre będą obsługiwane przez (miejmy nadzieję) zainstalowany Structed Exception Handling Handler
user45891
1
Czasami robią coś innego niż gwarancja awarii aplikacji. Na przykład wiele języków / platform gwarantuje, że zgłoszą wyjątek dzielenia przez zero. Następnie możesz złapać i obsłużyć wspomniany wyjątek bez awarii.
reirab
2
Możesz usunąć „może” w obrębie „Inne procesory mogą zachowywać się inaczej”: Na platformie PowerPC podział po prostu daje wynik zerowy przy dzieleniu przez zero. Co jest znacznie bardziej przydatne niż zachowanie paniki platformy X86.
cmaster
13

Wygląda na to, że zastanawiasz się, co by się stało, gdyby ktoś wykonał procesor, który nie sprawdza wprost zerowania przed podziałem. To, co by się stało, zależy całkowicie od wdrożenia podziału. Bez wchodzenia w szczegóły jeden rodzaj implementacji dałby wynik, który ma ustawione wszystkie bity, np. 65535 na 16-bitowym procesorze. Inny może się rozłączyć.

Bingo
źródło
1

Zastanawiam się jednak, czy komputer nawet próbuje podzielić przez zero, czy też po prostu ma „wbudowaną ochronę”, więc gdy „widzi” 0/0, zwraca błąd nawet przed próbą jego obliczenia?

Ponieważ x/0nie ma sensu, kropka, komputery zawsze muszą sprawdzać podział przez zero. Jest tu problem: programiści chcą obliczać (a+b)/cbez konieczności sprawdzania, czy te obliczenia mają sens. Podstawowa reakcja na dzielenie przez zero przez procesor + typ liczb + system operacyjny + język polega na zrobieniu czegoś dość drastycznego (np. Awaria programu) lub zrobieniu czegoś zbyt łagodnego (np. Utworzenie wartości, która nie powoduje sens, taki jak zmiennoprzecinkowy IEEE NaN, liczba „Nie jest liczbą”).

W zwykłym otoczeniu programista powinien wiedzieć, czy (a+b)/cma to sens. W tym kontekście nie ma powodu, aby sprawdzać podział przez zero. Jeśli dojdzie do podziału przez zero i jeśli język maszynowy + język implementacji + typ danych + odpowiedź systemu operacyjnego na to spowoduje awarię programu, to jest w porządku. Jeśli odpowiedzią jest utworzenie wartości, która może ostatecznie skazić każdą liczbę w programie, to też jest w porządku.

Ani „coś drastycznego”, ani „zbyt łagodnego” nie jest właściwą rzeczą w świecie komputerów o wysokiej niezawodności. Te domyślne odpowiedzi mogą zabić pacjenta, rozbić samolot lub spowodować wybuch bomby w niewłaściwym miejscu. W środowisku o wysokiej niezawodności programista, który pisze, (a+b)/czostanie zabity podczas przeglądania kodu, lub w czasach współczesnych, być może automatycznie zabity przez narzędzie sprawdzające konstrukcje verboten. W tym środowisku programista powinien zamiast tego napisać coś w stylu div(add(a,b),c)(i ewentualnie sprawdzając status błędu). Pod maską div(/ również add) funkcje / makra chronią przed dzieleniem przez zero (lub przepełnieniem w przypadku add). To, co obejmuje ta ochrona, jest ściśle związane z implementacją.

David Hammen
źródło
To, że NaN nie przestrzega arytmetyki, której nauczyłeś się w szkole, nie oznacza, że nie ma to sensu. Stosuje się do innej arytmetyki
Caleth
-2

Wiemy już o tym x/0i 0/0nie mamy dobrze zdefiniowanych odpowiedzi. Co się stanie, jeśli 0/0mimo to spróbujesz obliczyć ?

W nowoczesnym systemie obliczenia są przekazywane do jednostki MPU w procesorze i są oznaczane jako operacja niedozwolona, ​​zwracana NaN.

W dużo starszym systemie, takim jak komputery domowe z lat 80., które nie miały podziału na chipy, obliczenia były wykonywane przez dowolne oprogramowanie. Istnieje kilka możliwych opcji:

  • Odejmuj coraz mniejsze kopie dzielnika, aż wartość osiągnie zero, i śledź, które kopie w rozmiarze zostały odjęte
    • Jeśli sprawdzi zero przed pierwszym odejmowaniem, wyjdzie szybko i wynikiem będzie 0
    • Jeśli zakłada, że ​​musi być w stanie odjąć co najmniej raz, wynikiem będzie 1
  • Oblicz logarytmy obu liczb, odejmij je i zwiększ e do potęgi wyniku. Metoda bardzo nieefektywna w porównaniu do powyższej metody odejmowania, ale poprawna matematycznie
    • Może wystąpić przepełnienie przy próbie obliczenia, log(0)a oprogramowanie albo użyje procedur obsługi błędów, albo ulegnie awarii
    • Oprogramowanie może założyć, że wszystkie logarytmy można obliczyć w określonej liczbie kroków i zwrócić dużą, ale niepoprawną wartość. Ponieważ oba logarytmy byłyby takie same, różnica wynosiłaby 0e 0 = 1, dając wynik1

Innymi słowy, zależałoby od implementacji, co się stanie, i byłoby możliwe napisanie oprogramowania, które daje poprawne i przewidywalne wyniki dla każdej wartości, ale pozornie dziwne wartości, 0/0które są jednak nadal wewnętrznie spójne.

CJ Dennis
źródło
3
To pytanie dotyczy dzielenia przez zero w liczbach całkowitych i nie ma czegoś takiego jak NaNw liczbach całkowitych.
David Hammen
3
Żaden procesor nie będzie obliczał dzienników ani odejmował, aby obliczyć wynik podziału. Czas instrukcji logarytmu jest o kilka rzędów wielkości większy niż podział. Jaka jest wartość log (0)?
wallyk
1
@DavidHammen OP ani razu nie wspomniał o liczbach całkowitych, ani też nikt, kto skomentował to pytanie. Liczby całkowite są wymienione tylko w odpowiedziach.
CJ Dennis
@wallyk W swojej odpowiedzi powiedziałem już, że logarytmy służą very inefficientdo podziału. Koszt arytmetyki wynosi (dodawanie = odejmowanie) <= mnożenie <= dzielenie. Jeśli nie masz jednostki MPU, która może wykonywać dzielenie w tej samej liczbie cykli zegara, co dodawanie (zwykle jeden), dzielenie jest droższe niż dodawanie i odejmowanie i zwykle również droższe niż mnożenie.
CJ Dennis