Dlaczego dzielenie int.MinValue by -1 throwed OverflowException in unchecked context?

int y = -2147483648;
int z = unchecked(y / -1);

Druga linia powoduje OverflowException. Czy nie powinno się temu zapobiec?

Na przykład:

int y = -2147483648;
int z = unchecked(y * 2);

Nie powoduje wyjątku.

Author: BartoszKP, 2014-10-27

3 answers

Nie jest to wyjątek, nad którym kompilator C# lub jitter mają jakąkolwiek kontrolę. Jest to specyficzne dla procesorów Intel / AMD, Procesor generuje # de trap (Divide Error), gdy instrukcja IDIV nie powiedzie się. System operacyjny obsługuje pułapkę procesora i odzwierciedla ją z powrotem do procesu za pomocą wyjątku STATUS_INTEGER_OVERFLOW. CLR posłusznie przekłada go na dopasowany zarządzany wyjątek.

Instrukcja procesora Intela nie jest dokładnie kopalnią złota informacji o it:

Wyniki nieintegralne są obcięte (posiekane) w kierunku 0. Reszta jest zawsze mniejsza od dzielnika wielkości. Przepełnienie jest oznaczone wyjątkiem #DE (divide error), a nie flagą CF.

W języku angielskim: wynik podpisanego podziału wynosi +2147483648, nie można go reprezentować w int , ponieważ jest to Int32.MaxValue + 1. W przeciwnym razie nieuniknionym efektem ubocznym sposobu, w jaki procesor reprezentuje wartości ujemne, używa dopełnienia dwóch kodowanie. Co daje pojedynczą wartość reprezentującą 0, pozostawiając nieparzystą liczbę innych możliwych kodowań reprezentujących wartości ujemne i dodatnie. Jest jeszcze jeden dla wartości ujemnych. Taki sam rodzaj przepełnienia jak -Int32.MinValue, z tym, że procesor nie blokuje instrukcji NEG i po prostu generuje wynik śmieci.

Język C# nie jest oczywiście jedynym z tym problemem. Spec języka C# sprawia, że implementacja definiuje zachowanie (rozdział 7.8.2), zwracając uwagę na specjalne zachowanie. Żadna inna rozsądna rzecz nie mogła z tym zrobić, generowanie kodu do obsługi wyjątku z pewnością było uważane za zbyt niepraktyczne, produkując niezdiagnozowanie powolny kod. Nie sposób na C#.

Język C i C++ zwiększa ante, czyniąc go niezdefiniowanym zachowaniem. To może być naprawdę brzydkie, jak program skompilowany z kompilatorem gcc lub G++, zazwyczaj z łańcuchem narzędzi MinGW. Który ma niedoskonałą obsługę runtime dla SEH, połyka wyjątek i pozwala procesorowi na uruchom ponownie instrukcję dywizji. Program zawiesza się, wypalając 100% rdzeń z procesorem stale generującym pułapki # DE. Przekształcenie podziału w legendarną instrukcję Halt and Catch Fire :)

 31
Author: Hans Passant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-28 11:50:26

Sekcja 7.72 (Operator podziału) specyfikacji C# 4 stwierdza:

Jeśli lewy operand jest najmniejszą reprezentowalną wartością int lub long, a prawy operand to -1, następuje przepełnienie. W sprawdzonym kontekście [...]. W niekontrolowanym kontekście jest to implementacja-definiowana jako Czy System.ArithmeticException (lub jego podklasa) jest wyrzucany lub przepełnienie nie jest raportowane, a wynikową wartością jest lewy operand.

Więc fakt, że to rzuca wyjątek w niekontrolowanym kontekście nie jest w rzeczywistości błędem, ponieważ zachowanie jest zdefiniowane w implementacji.

 33
Author: Servy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-28 13:48:32

Zgodnie z sekcją 7.8.2 specyfikacji języka C# 5.0 mamy następujący przypadek:

7.8.2 Operator Dywizji
Dla operacji postaci x / y przeciążenie operatora binarnego uchwałę (§7.3.4) stosuje się do wyboru konkretnego operatora wdrożenie. Operandów są konwertowane na typy parametrów wybranego operatora, a typem wyniku jest typ powrotu operatora. Predefiniowanymi operatorami podziału są wymienione poniżej. Wszystkie operatory obliczają iloraz x i y.

  • podział liczb całkowitych:
    int operator /(int x, int y);
    uint operator /(uint x, uint y);
    long operator /(long x, long y);
    ulong operator /(ulong x, ulong y);
    Jeśli wartość prawego operandu wynosi zero, wyrzuca się System.DivideByZeroException. Podział zaokrągla wynik do zera. Tak więc wartość bezwzględna wyniku jest największą możliwą liczbą całkowitą, która jest mniejsza lub równa wartości bezwzględnej ilorazu obu operandów. Wynik jest zerowy lub dodatni, gdy dwa operandy mają ten sam znak i zero lub minus, gdy dwa operandy mają przeciwne znaki. jeśli lewy operand jest najmniejszą reprezentowalną wartością int lub long, a prawy operand to -1, następuje przepełnienie. W sprawdzonym kontekście powoduje to wyrzucenie System.ArithmeticException (lub jej podklasy). w kontekście niezaznaczonym, jest to implementacja definiowana jako to, czy System.ArithmeticException (lub jej podklasa) jest wyrzucana, czy przepełnienie nie jest raportowane, a wynikowa wartość jest wartością lewej operand.
 10
Author: Markus Safar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-27 19:22:10