Jak poradzić sobie z dzieleniem przez zero w języku, który nie obsługuje wyjątków?

62

Jestem w trakcie opracowywania nowego języka programowania, aby rozwiązać niektóre wymagania biznesowe, a ten język jest skierowany do początkujących użytkowników. Dlatego nie ma obsługi obsługi wyjątków w tym języku i nie spodziewałbym się, że będą go używać, nawet jeśli go dodam.

Doszedłem do punktu, w którym muszę wdrożyć operator dzielenia i zastanawiam się, jak najlepiej obsłużyć błąd dzielenia przez zero?

Wydaje mi się, że mam tylko trzy możliwe sposoby rozwiązania tej sprawy.

  1. Zignoruj ​​błąd i wygeneruj 0jako wynik. Rejestrowanie ostrzeżenia, jeśli to możliwe.
  2. Dodaj NaNjako możliwą wartość dla liczb, ale to rodzi pytania dotyczące sposobu obsługi NaNwartości w innych obszarach języka.
  3. Zakończ wykonywanie programu i zgłoś użytkownikowi poważny błąd.

Opcja nr 1 wydaje się jedynym rozsądnym rozwiązaniem. Opcja nr 3 nie jest praktyczna, ponieważ ten język będzie używany do uruchamiania logiki jako nocny cron.

Jakie są moje alternatywy dla obsługi błędu dzielenia przez zero i jakie jest ryzyko związane z opcją nr 1.

Reactgular
źródło
12
jeśli dodałeś obsługę wyjątków, a użytkownik jej nie złapał, miałbyś opcję 3
maniak ratchet
82
Ciekawe, jaki głupi wymóg wymagałby od ciebie stworzenia zupełnie nowego języka programowania? Z mojego doświadczenia, każdy język kiedykolwiek stworzony jest do bani (w projektowaniu lub w realizacji, często w obu) i zajęło bezzasadnie wiele wysiłku, aby dostać nawet , że dużo. Jest kilka wyjątków od pierwszego, ale nie do drugiego, a ponieważ są one z łatwością <0,01% przypadków, prawdopodobnie są to błędy pomiarowe ;-)
16
@delnan powstaje większość nowych języków, aby umożliwić oddzielenie reguł biznesowych od sposobu ich implementacji. Użytkownik nie musi wiedzieć, w jaki sposób reject "Foo"został zaimplementowany, ale po prostu odrzuca dokument, jeśli zawiera słowo kluczowe Foo. Staram się uczynić ten język tak łatwym do odczytania za pomocą terminów, które użytkownik zna. Nadanie użytkownikowi własnego języka programowania pozwala mu dodawać reguły biznesowe bez uzależnienia od personelu technicznego.
Reactgular,
19
@Mathew Foscarini. Nigdy, nigdy, nie ignoruj ​​błędu i po cichu zwróć 0. Podczas dzielenia, 0 może być wartością całkowicie zgodną z prawem (z jakiegoś powodu w Power Basic jest coś takiego i to naprawdę jest ból). Jeśli podzielisz liczby zmiennoprzecinkowe, Nan lub Inf byłoby fajne (spójrz na IEEE 754, aby zrozumieć, dlaczego). Jeśli podzielisz liczby całkowite, możesz zatrzymać program, dzielenie przez 0 nigdy nie powinno być dozwolone (cóż, chyba że chcesz wdrożyć prawdziwy system wyjątków).
16
Jestem rozbawiony i zafascynowany kompleksem domen biznesowych wystarczająco uzasadniającym zastrzeżony, kompletny język programowania Turinga, ale wystarczająco swobodny, aby tolerować drastycznie niedokładne wyniki.
Mark E. Haase,

Odpowiedzi:

98

Zdecydowanie odradzałbym # 1, ponieważ ignorowanie błędów jest niebezpiecznym anty-wzorem. Może to prowadzić do trudnych do analizy błędów. Ustawienie wyniku dzielenia przez zero na zero nie ma żadnego sensu, a kontynuowanie wykonywania programu z bezsensowną wartością spowoduje kłopoty. Zwłaszcza, gdy program działa bez nadzoru. Gdy interpreter programu zauważy, że w programie wystąpił błąd (a dzielenie przez zero prawie zawsze jest błędem projektowym), zwykle przerywa się go i utrzymuje wszystko w niezmienionym stanie, niż wypełnianie bazy danych śmieciami.

Ponadto, prawdopodobnie nie odniesiesz sukcesu przy dokładnym przestrzeganiu tego wzoru. Wcześniej czy później napotkasz sytuacje błędów, których po prostu nie można zignorować (na przykład brak pamięci lub przepełnienie stosu) i będziesz musiał zaimplementować sposób zakończenia programu.

Opcja nr 2 (użycie NaN) byłaby trochę pracochłonna, ale nie tak bardzo, jak mogłoby się wydawać. Sposób obsługi NaN w różnych obliczeniach jest dobrze udokumentowany w standardzie IEEE 754, więc prawdopodobnie możesz po prostu robić to, w jakim języku jest napisany Twój tłumacz.

Nawiasem mówiąc: Tworzenie języka programowania używanego przez nieprogramowych programistów jest czymś, co próbowaliśmy robić od 1964 roku (Dartmouth BASIC). Jak dotąd nie udało nam się. Ale i tak powodzenia.

Philipp
źródło
14
+1 dzięki. Przekonałeś mnie, żebym rzucił błąd, a teraz, kiedy przeczytałem twoją odpowiedź, nie rozumiem, dlaczego się wahałem. PHPmiał na mnie zły wpływ.
Reactgular,
24
Tak, to ma. Kiedy przeczytałem twoje pytanie, od razu pomyślałem, że bardzo źle wyglądało PHP, aby generować niepoprawne dane wyjściowe i kontynuować jazdę w obliczu błędów. Istnieją dobre powody, dla których PHP jest wyjątkiem.
Joel
4
+1 za komentarz PODSTAWOWY. Nie radzę używać NaNw języku początkującym, ale ogólnie świetna odpowiedź.
Ross Patterson,
8
@Joel Gdyby żył wystarczająco długo, Dijkstra prawdopodobnie powiedziałby: „Zastosowanie [PHP] kaleczy umysł; dlatego jego nauczanie powinno być traktowane jako przestępstwo”.
Ross Patterson,
12
@Ross. „arogancja w informatyce jest mierzona w nano-Dijkstrasie” - Alan Kay
33

1 - Zignoruj ​​błąd i wygeneruj 0jako wynik. Rejestrowanie ostrzeżenia, jeśli to możliwe.

To nie jest dobry pomysł. W ogóle. Ludzie zaczną w zależności od tego i jeśli kiedykolwiek to naprawisz, złamiesz dużo kodu.

2 - Dodaj NaNjako możliwą wartość dla liczb, ale to rodzi pytania dotyczące sposobu obsługi NaNwartości w innych obszarach języka.

Powinieneś obsługiwać NaN tak, jak robią to środowiska wykonawcze innych języków: Każde dalsze obliczenia również dają NaN, a każde porównanie (nawet NaN == NaN) daje fałsz.

Myślę, że jest to do przyjęcia, ale niekoniecznie nowe przyjazne dla początkujących.

3 - Zakończ wykonywanie programu i zgłoś użytkownikowi wystąpienie poważnego błędu.

Myślę, że to najlepsze rozwiązanie. Mając te informacje w ręku, użytkownicy powinni być w stanie obsłużyć 0. Powinieneś zapewnić środowisko testowe, zwłaszcza jeśli ma ono działać raz na noc.

Jest też czwarta opcja. Niech podział będzie operacją potrójną. Każdy z tych dwóch będzie działać:

  • div (licznik, denumerator, alternatywny_wynik)
  • div (licznik, denumerator, altern_denumerator)
back2dos
źródło
Ale jeśli NaN == NaNbędzie false, to trzeba będzie dodać isNaN()funkcję, dzięki czemu użytkownicy są w stanie wykryć NaNs.
AJMansfield
2
@AJMansfield: Albo to, albo ludzie wdrożyć go sami: isNan(x) => x != x. Mimo to, kiedy pojawia NaNsię kod programowania, nie powinieneś zaczynać dodawać isNaNkontroli, a raczej wyśledzić przyczynę i dokonać niezbędnych kontroli. Dlatego ważne jest, NaNaby propagować w pełni.
back2dos,
5
NaNsą głównie sprzeczne z intuicją. W języku początkującym są martwi po przyjeździe.
Ross Patterson,
2
@RossPatterson Ale początkujący może łatwo powiedzieć 1/0- musisz coś z tym zrobić. Nie ma potencjalnie użyteczny wynik inny niż Influb NaN- coś, co będzie dalej propagować błąd w programie. W przeciwnym razie jedynym rozwiązaniem jest zatrzymanie się z błędem w tym momencie.
Mark Hurd
1
Wariant 4 można ulepszyć, umożliwiając wywołanie funkcji, która z kolei może wykonać wszelkie czynności niezbędne do odzyskania od nieoczekiwanego dzielnika 0.
CyberFonic
21

Zakończ działającą aplikację z wyjątkowymi uprzedzeniami. (Podając odpowiednie informacje debugowania)

Następnie naucz użytkowników, jak identyfikować i obsługiwać warunki, w których dzielnik może wynosić zero (wartości wprowadzone przez użytkownika itp.)

Dave Nay
źródło
13

W Haskell (i podobnym w Scali) zamiast zgłaszania wyjątków (lub zwracania referencji zerowych) można zastosować typy opakowań Maybei Eithermożna je stosować. Dzięki Maybetemu użytkownik ma szansę sprawdzić, czy wartość, którą otrzymał, jest „pusta” lub może podać wartość domyślną podczas „rozpakowywania”. Eitherjest podobny, ale można go użyć zwraca obiekt (np. ciąg błędu) opisujący problem, jeśli taki istnieje.

Landei
źródło
1
To prawda, ale zauważ, że Haskell nie używa tego do dzielenia przez zero. Zamiast tego każdy typ Haskella domyślnie ma „dolną” jako możliwą wartość. To nie jest jak wskaźniki zerowe w tym sensie, że jest to „wartość” wyrażenia, które się nie kończy. Oczywiście nie można testować nieterminacji jako wartości, ale w semantyce operacyjnej przypadki, które się nie kończą, są częścią znaczenia wyrażenia. W Haskell ta „dolna” wartość obsługuje również dodatkowe wyniki w przypadku błędu, takie jak error "some message"oceniana funkcja.
Steve314,
Osobiście, jeśli efekt przerwania całego programu zostanie uznany za prawidłowy, nie wiem, dlaczego czysty kod nie może powodować rzucania wyjątku, ale to tylko ja - Haskellnie pozwala czystym wyrażeniom na zgłaszanie wyjątków.
Steve314,
Myślę, że to dobry pomysł, ponieważ poza zgłoszeniem wyjątku, wszystkie proponowane opcje nie informują użytkowników, że popełnili błąd. Podstawową ideą jest to, że użytkownik popełnia błąd przy wartości, którą podał programowi, więc program powinien poinformować użytkownika, że ​​podał nieprawidłowe dane wejściowe (wtedy użytkownik może wymyślić sposób na zaradzenie). Bez informowania użytkowników o pomyłce każde rozwiązanie wydaje się tak dziwne.
InformedA
Myślę, że to jest właściwy sposób ... Język programowania Rust używa go szeroko w swojej standardowej bibliotece.
aochagavia
12

Inne odpowiedzi już rozważały względne zalety twoich pomysłów. Proponuję inny: użyj podstawowej analizy przepływu, aby ustalić, czy zmienna może wynosić zero. Następnie możesz po prostu zabronić dzielenia według zmiennych, które są potencjalnie zerowe.

x = ...
y = ...

if y ≠ 0:
  return x / y    // In this block, y is known to be nonzero.
else:
  return x / y    // This, however, is a compile-time error.

Alternatywnie, mieć inteligentną funkcję potwierdzenia, która ustanawia niezmienniki:

x = ...
require x ≠ 0, "Unexpected zero in calculation"
// For the remainder of this scope, x is known to be nonzero.

Jest to tak dobre, jak zgłoszenie błędu czasu wykonywania - całkowicie pomija się niezdefiniowane operacje - ale ma tę zaletę, że ścieżka kodu nie musi nawet zostać trafiona, aby możliwe było ujawnienie potencjalnej awarii. Można to zrobić podobnie jak zwykłe sprawdzanie typów, oceniając wszystkie gałęzie programu z zagnieżdżonymi środowiskami do pisania w celu śledzenia i weryfikacji niezmienników:

x = ...           // env1 = { x :: int }
y = ...           // env2 = env1 + { y :: int }
if y ≠ 0:         // env3 = env2 + { y ≠ 0 }
  return x / y    // (/) :: (int, int ≠ 0) → int
else:             // env4 = env2 + { y = 0 }
  ...
...               // env5 = env2

Ponadto naturalnie rozszerza się na zasięg i nullsprawdzanie, czy Twój język ma takie funkcje.

Jon Purdy
źródło
4
Świetny pomysł, ale ten rodzaj rozwiązywania ograniczeń jest NP-kompletny. Wyobraź sobie coś takiego def foo(a,b): return a / ord(sha1(b)[0]). Analizator statyczny nie może odwrócić SHA-1. Clang ma tego rodzaju analizę statyczną i doskonale nadaje się do wyszukiwania płytkich błędów, ale jest wiele przypadków, z którymi nie może sobie poradzić.
Mark E. Haase
9
to nie jest NP-zupełne, to niemożliwe - mówi zatrzymanie lematu. Jednak analizator statyczny nie musi tego rozwiązywać, może po prostu zepsuć takie stwierdzenie i wymagać wyraźnego potwierdzenia lub dekoracji.
MK01
1
@ MK01: Innymi słowy, analiza jest „konserwatywna”.
Jon Purdy,
11

Liczba 1 (wstaw niezbadane zero) jest zawsze zła. Wybór między numerem 2 (propagacja NaN) a numerem 3 (zabicie procesu) zależy od kontekstu i najlepiej powinien być ustawieniem globalnym, tak jak ma to miejsce w Numpy.

Jeśli wykonujesz jedno duże, zintegrowane obliczenie, propagowanie NaN jest złym pomysłem, ponieważ ostatecznie rozprzestrzeni się i zainfekuje całe twoje obliczenia --- kiedy spojrzysz na wyniki rano i zobaczysz, że wszystkie są NaN, „ d i tak muszę wyrzucić wyniki i zacząć od nowa. Byłoby lepiej, gdyby program się zakończył, dostałeś telefon w środku nocy i naprawiłeś go - przynajmniej pod względem liczby zmarnowanych godzin.

Jeśli wykonujesz wiele niewielkich, w większości niezależnych obliczeń (takich jak pomniejszenie mapy lub żenująco równoległe obliczenia) i możesz tolerować pewien procent z nich, które nie nadają się do użycia z powodu NaN, jest to prawdopodobnie najlepsza opcja. Zakończenie programu i niewykonanie 99%, co byłoby dobre i przydatne ze względu na zniekształcenie 1% i podzielenie przez zero, może być błędem.

Inna opcja związana z NaN: ta sama specyfikacja zmiennoprzecinkowa IEEE definiuje Inf i -Inf i są one propagowane inaczej niż NaN. Na przykład jestem całkiem pewien, że Inf> dowolna liczba i -Inf <dowolna liczba, co byłoby tym, czego chciałbyś, gdyby twój podział przez zero nastąpił, ponieważ zero miało być tylko małą liczbą. Jeśli dane wejściowe są zaokrąglone i występują błędy pomiaru (takie jak pomiary fizyczne wykonywane ręcznie), różnica dwóch dużych ilości może spowodować zero. Bez dzielenia przez zero uzyskałbyś dużą liczbę i być może nie obchodzi Cię, jak duża jest. W takim przypadku In i -Inf są perfekcyjnie poprawnymi wynikami.

To może być formalnie poprawne - po prostu powiedz, że pracujesz w rozszerzonych realiach.

Jim Pivarski
źródło
Ale nie możemy powiedzieć, czy mianownik miał być dodatni czy ujemny, więc podział może dać + inf, gdy pożądane jest -inf, lub odwrotnie.
Daniel Lubarov
To prawda, że ​​błąd pomiaru jest zbyt mały, aby można go było odróżnić od + inf i -inf. To najbardziej przypomina kulę Riemanna, w której cała płaszczyzna złożona jest odwzorowana na kulę z dokładnie jednym nieskończonym punktem (punkt diametralnie przeciwny do początku). Bardzo duże liczby dodatnie, bardzo duże liczby ujemne, a nawet bardzo duże liczby urojone i złożone są zbliżone do tego jednego nieskończonego punktu. Przy niewielkim błędzie pomiaru nie można ich rozróżnić.
Jim Pivarski
Jeśli pracujesz w tego rodzaju systemie, musisz zidentyfikować + inf i -inf jako równoważne, podobnie jak musisz zidentyfikować +0 i -0 jako równoważne, nawet jeśli mają różne reprezentacje binarne.
Jim Pivarski
8

3. Zakończ wykonywanie programu i zgłoś użytkownikowi poważny błąd.

[Ta opcja] nie jest praktyczna…

Oczywiście jest to praktyczne: programiści mają obowiązek napisania programu, który ma sens. Dzielenie przez 0 nie ma sensu. Dlatego, jeśli programista wykonuje podział, jego obowiązkiem jest również wcześniejsze sprawdzenie , czy dzielnik nie jest równy 0. Jeśli programista nie wykona tej kontroli sprawdzania poprawności, powinien on / ona zdać sobie sprawę z tego błędu, jak tylko możliwe i zdenormalizowane (NaN) lub niepoprawne (0) wyniki obliczeń po prostu nie pomogą w tym zakresie.

Zdarza się, że opcja 3 jest tą, którą poleciłbym wam, ponieważ jest najbardziej prosta, uczciwa i poprawna matematycznie.

stakx
źródło
4

Wydaje mi się, że to zły pomysł na uruchamianie ważnych zadań (np. „Night cron”) w środowisku, w którym błędy są ignorowane. To straszny pomysł, aby włączyć tę funkcję. To wyklucza opcje 1 i 2.

Opcja 3 jest jedynym dopuszczalnym rozwiązaniem. Wyjątki nie muszą być częścią języka, ale są częścią rzeczywistości. Twoja wiadomość o zakończeniu powinna być jak najbardziej szczegółowa i zawierać informacje o błędzie.

ddyer
źródło
3

IEEE 754 faktycznie ma dobrze zdefiniowane rozwiązanie twojego problemu. Obsługa wyjątków bez użycia exceptions http://en.wikipedia.org/wiki/IEEE_floating_point#Exception_handling

1/0  = Inf
-1/0 = -Inf
0/0  = NaN

w ten sposób wszystkie twoje operacje mają matematyczny sens.

\ lim_ {x \ do 0} 1 / x = Inf

Moim zdaniem przestrzeganie IEEE 754 jest najbardziej sensowne, ponieważ zapewnia, że ​​twoje obliczenia są tak poprawne, jak na komputerze, a także jesteś zgodny z zachowaniem innych języków programowania.

Jedynym problemem, który się pojawia, jest to, że Inf i NaN będą zanieczyszczać twoje wyniki, a twoi użytkownicy nie będą dokładnie wiedzieć, skąd pochodzi problem. Spójrz na język taki jak Julia, który robi to całkiem dobrze.

julia> 1/0
Inf

julia> -1/0
-Inf

julia> 0/0
NaN

julia> a = [1,1,1] ./ [2,1,0]
3-element Array{Float64,1}:
   0.5
   1.0
 Inf

julia> sum(a)
Inf

julia> a = [1,1,0] ./ [2,1,0]
3-element Array{Float64,1}:
   0.5
   1.0
 NaN

julia> sum(a)
NaN

Błąd podziału jest poprawnie propagowany przez operacje matematyczne, ale na końcu użytkownik niekoniecznie wie, z której operacji wynika błąd.

edit:Nie widziałem drugiej części odpowiedzi Jima Pivarskiego, która jest zasadniczo tym, co mówię powyżej. Mój błąd.

wallnuss
źródło
2

SQL, z łatwością język najczęściej używany przez nieprogramowych programistów, zajmuje trzecie miejsce, niezależnie od tego, co jest tego warte. Z mojego doświadczenia obserwując i pomagając nie-programistom piszącym SQL, takie zachowanie jest ogólnie dobrze zrozumiałe i łatwo kompensowane (za pomocą instrukcji case lub podobnego). Pomaga to, że otrzymany komunikat o błędzie wydaje się być dość bezpośredni, np. W Postgres 9 pojawia się komunikat „BŁĄD: dzielenie przez zero”.

Noah Yetter
źródło
2

Myślę, że problem jest „skierowany do początkujących użytkowników. -> Więc nie ma wsparcia dla ...”

Dlaczego uważasz, że obsługa wyjątków jest problematyczna dla początkujących użytkowników?

Co jest gorsze? Masz „trudną” funkcję lub nie masz pojęcia, dlaczego coś się stało? Co może dezorientować więcej? Awaria z zrzutem rdzenia lub „Błąd krytyczny: Dziel przez zero”?

Zamiast tego myślę, że DALEJ lepiej dążyć do WIELKICH błędów komunikatów. Zamiast tego: „Złe obliczenia, podziel 0/0” (tzn .: Zawsze pokazuj DANE, które powodują problem, a nie tylko rodzaj problemu). Zobacz, jak PostgreSql robi błędy komunikatów, które są świetne IMHO.

Możesz jednak spojrzeć na inne sposoby pracy z wyjątkami, takie jak:

http://dlang.org/exception-safe.html

Marzę również o zbudowaniu języka, i w tym przypadku myślę, że połączenie Może / Opcjonalne z normalnymi Wyjątkami może być najlepsze:

def openFile(fileName): File | Exception
    if not(File.Exist(fileName)):
        raise FileNotExist(fileName)
    else:
        return File.Open()

#This cause a exception:

theFile = openFile('not exist')

# But this, not:

theFile | err = openFile('not exist')
mamcx
źródło
1

Moim zdaniem twój język powinien zapewniać ogólny mechanizm wykrywania i obsługi błędów. Błędy programowania powinny być wykryte w czasie kompilacji (lub tak wcześnie, jak to możliwe) i zwykle powinny prowadzić do zakończenia programu. Błędy, które wynikają z nieoczekiwanych lub błędnych danych lub z nieoczekiwanych warunków zewnętrznych, powinny zostać wykryte i udostępnione dla odpowiednich działań, ale pozwalają programowi na kontynuowanie w miarę możliwości.

Możliwe działania obejmują (a) zakończ (b) poproś użytkownika o działanie (c) zaloguj błąd (d) zastąp poprawioną wartość (e) ustaw wskaźnik do przetestowania w kodzie (f) wywołaj procedurę obsługi błędu. Które z nich udostępniasz i za pomocą jakich środków musisz dokonać wyboru.

Z mojego doświadczenia wynika, że ​​typowe błędy danych, takie jak wadliwe konwersje, dzielenie przez zero, przepełnienie i wartość poza zakresem są łagodne i powinny być domyślnie obsługiwane przez podstawienie innej wartości i ustawienie flagi błędu. (Nieprogramiści) używający tego języka zobaczą wadliwe dane i szybko zrozumieją potrzebę sprawdzenia błędów i obsługi ich.

[Na przykład rozważ arkusz kalkulacyjny Excel. Program Excel nie kończy arkusza kalkulacyjnego, ponieważ liczba została przelana lub cokolwiek innego. Komórka ma dziwną wartość, a ty dowiedz się, dlaczego ją naprawisz.]

Aby odpowiedzieć na twoje pytanie: z pewnością nie powinieneś kończyć. Możesz zastąpić NaN, ale nie powinieneś tego robić, tylko upewnij się, że obliczenia się zakończyły i wygenerowały dziwną wysoką wartość. I ustaw flagę błędu, aby użytkownicy, którzy jej potrzebują, mogli ustalić, że wystąpił błąd.

Ujawnienie: Stworzyłem właśnie taką implementację języka (Powerflex) i dokładnie rozwiązałem ten problem (i wiele innych) w latach 80. W ciągu ostatnich 20 lat postęp w dziedzinie języków był niewielki lub żaden. Dla nieprogramiści postępy są niewielkie, a ty będziesz przyciągał mnóstwo krytyki za próby, ale mam nadzieję, że ci się uda.

david.pfx
źródło
1

Podobał mi się trójskładnikowy operator, w którym podajesz alternatywną wartość w przypadku, gdy licznik wynosi 0.

Jeszcze jeden pomysł, którego nie widziałem, to wygenerowanie ogólnej „nieprawidłowej” wartości. Ogólne „ta zmienna nie ma wartości, ponieważ program zrobił coś złego”, który sam przenosi ślad pełnego stosu. Następnie, jeśli kiedykolwiek użyjesz tej wartości w dowolnym miejscu, wynik jest ponownie niepoprawny, a nowa operacja zostanie podjęta na górze (tj. Jeśli niepoprawna wartość kiedykolwiek pojawi się w wyrażeniu, całe wyrażenie zwróci wartość niepoprawną i nie będą podejmowane żadne wywołania funkcji; wyjątek być operatorami logicznymi - prawda lub niepoprawność to prawda, a fałsz, a niepoprawność to fałsz - mogą istnieć również inne wyjątki). Gdy nigdzie nie ma już takiej wartości, zapisujesz ładny długi opis całego łańcucha, w którym coś jest nie tak, i kontynuujesz biznes jak zwykle. Może wysłać ślad e-mailem do kierownika projektu lub coś takiego.

Zasadniczo coś w rodzaju monady Może. Będzie działał ze wszystkim, co również może zawieść, i możesz pozwolić ludziom na budowę własnych inwalidów. I program będzie działał tak długo, jak długo błąd nie będzie zbyt głęboki, tak myślę, że naprawdę tego tutaj potrzebujemy.

Moszewa
źródło
1

Istnieją dwa podstawowe powody podziału przez zero.

  1. W precyzyjnym modelu (np. Liczbach całkowitych) otrzymujesz podział przez zero DBZ, ponieważ dane wejściowe są nieprawidłowe. Jest to rodzaj DBZ, o którym myśli większość.
  2. W modelu nieprecyzyjnym (np. Zmiennoprzecinkowym pt) możesz otrzymać DBZ z powodu błędu zaokrąglenia, nawet jeśli dane wejściowe są prawidłowe. Tego zwykle nie myślimy.

Po 1. musisz poinformować użytkowników, że popełnili błąd, ponieważ to oni są odpowiedzialni i to oni najlepiej wiedzą, jak naprawić sytuację.

W przypadku 2. To nie jest wina użytkownika, możesz wskazać algorytm, implementację sprzętu itp., Ale to nie jest wina użytkownika, więc nie powinieneś kończyć programu ani nawet rzucać wyjątku (jeśli jest to dozwolone, co nie jest w tym przypadku). Dlatego rozsądnym rozwiązaniem jest kontynuowanie operacji w rozsądny sposób.

Widzę, że osoba zadająca to pytanie zadała pytanie 1. Więc musisz się z powrotem skontaktować z użytkownikiem. Używanie dowolnego standardu zmiennoprzecinkowego, Inf, -Inf, Nan, IEEE nie pasuje do tej sytuacji. Zasadniczo zła strategia.

Poinformowano
źródło
0

Nie zezwalaj na to w języku. Innymi słowy, nie zezwalaj na dzielenie przez liczbę, dopóki nie będzie ona zerowa, zwykle najpierw testując ją. To znaczy.

int div = random(0,100);
int b = 10000 / div; // Error E0000: div might be zero
MSalters
źródło
Aby to zrobić, potrzebujesz nowego typu liczbowego, liczby naturalnej, w przeciwieństwie do liczby całkowitej. To może być ... trudne ... z czym sobie poradzić.
Servy
@Servy: Nie, nie zrobiłbyś tego. Dlaczego miałbyś? Potrzebujesz logiki w kompilatorze, aby określić możliwe wartości, ale i tak chcesz (ze względów optymalizacyjnych).
MSalters
Jeśli nie masz innego typu, jeden dla zera, a drugi dla wartości niezerowych, wtedy nie będziesz w stanie rozwiązać problemu w ogólnym przypadku. Miałbyś albo fałszywe alarmy i zmusiłbyś użytkownika do sprawdzania zera znacznie częściej niż powinien, albo stworzysz sytuacje, w których nadal będzie mógł dzielić przez zero.
Servy
@Servy: Mylisz się: kompilator może śledzić ten stan bez potrzeby posiadania takiego typu, na przykład GCC już to robi. Np. Typ C intdopuszcza wartości zerowe, ale GCC wciąż może określić, gdzie w kodzie ints nie może wynosić zero.
MSalters
2
Ale tylko w niektórych przypadkach; nie może tego zrobić ze 100% dokładnością we wszystkich przypadkach. Będziesz mieć fałszywie pozytywne lub fałszywe negatywy. Jest to możliwe do udowodnienia. Na przykład mógłbym utworzyć fragment kodu, który może, a nawet może nie zostać ukończony . Jeśli kompilator nie może nawet wiedzieć, czy się zakończył, to skąd może wiedzieć, czy wynikowa liczba int jest różna od zera? Może wychwycić proste oczywiste przypadki, ale nie wszystkie .
Servy
0

Pisząc język programowania, powinieneś skorzystać z tego faktu i uczynić obowiązkowym dołączenie akcji dla urządzenia przez stan zerowy. a <= n / c: 0 div-by-zero-action

Wiem, co właśnie zasugerowałem, w zasadzie dodając „goto” do twojego PL.

Stephen
źródło