2015-09-09 24 views

Odpowiedz

12

IIRC, operacja arytmetyczna między podpisaną i unsigned int da wynik unsigned.

Zatem 20 - 80u wytwarza unsigned równoważne wynikom -60: jeśli unsigned int jest typ 32-bitowe, to wynik jest 4294967236.

Nawiasem mówiąc, przydzielanie że do i1 wytwarza realizacji zdefiniowanej wyników, ponieważ liczba jest zbyt duży, aby zmieścić. Uzyskiwanie -60 jest typowe, ale nie gwarantowane.

+4

_Nawiasem mówiąc, przypisanie tej wartości do i1 jest niezdefiniowanym zachowaniem_ Czy jesteś tego pewien? Nauczyłem, że konwersja z unsigned int na int sygnowane jest dobrze zdefiniowana dla wszystkich wartości unsigned int. – rozina

+3

Tutaj nie ma przepełnionego liczby całkowitej ze znakiem. Istnieją konwersje. Zobacz [conv.integral] (http://eel.is/c++draft/conv.integral). – Sebivor

+0

@rozina: Huh, nigdy wcześniej nie widziałem, że nawrócenie działa inaczej pod tym względem. Naprawiono – Hurkyl

10
int i1 = 20-80u; // -60 

Argumenty są różne, a więc konieczna jest konwersja. Oba operandy są konwertowane na typ wspólny (w tym przypadku unsigned int). Wynik, który będzie dużą wartością unsigned int (60 mniej niż UINT_MAX + 1, jeśli moje obliczenia są poprawne) zostanie przekonwertowany na int, zanim zostanie zapisany w i1. Ponieważ ta wartość jest poza zakresem int, wynik będzie zdefiniowany jako implementacja, może być reprezentacją pułapki, a zatem może spowodować niezdefiniowane zachowanie podczas próby jej użycia. Jednak w twoim przypadku przypadkowo zamienia się na -60.


int i3 =(20-80u)/2; // 2147483618 

Kontynuując od pierwszego przykładu, moje przypuszczenie było, że wynik 20-80u będzie 60 mniej niż UINT_MAX + 1. Jeśli UINT_MAX to 4294967295 (wspólna wartość dla UINT_MAX), to oznaczałoby to 4294967236 ... 20-80u i 4294967236/2 jest 2147483618.


chodzi o i2 i inni, nie powinno być żadnych niespodzianek. Podążają za konwencjonalnymi obliczeniami matematycznymi bez konwersji, skracania lub przepełnienia.

+0

Więc jeśli rozumiem to poprawnie, konwersja -1 na unsigned jest dobrze zdefiniowana i jest UINT_MAX.Ale jeśli następnie przekonwertujesz UINT_MAX z powrotem na int, to nagle zostanie zdefiniowana implementacja? I nie może być -1? – rozina

+0

@rozina To prawda. – Sebivor

+0

Przyjemny dzień odpowiedzi :) – LPs

3

Binarne operatory arytmetyczne wykonają usual arithmetic conversions na swoich operandach w celu dostosowania ich do wspólnego typu.

W przypadku i1, i3 i i5 wspólna typu będzie unsigned int a więc wynik będzie również unsigned int. Niepodpisane liczby będą owijały się za pomocą arytmetyki modulo, więc odjęcie nieco większej, niepodpisanej wartości spowoduje, że liczba zbliżona do unsigned int max, która nie może być reprezentowana przez int.

Tak więc w przypadku i1 otrzymujemy zdefiniowaną implementację, ponieważ wartość nie może być reprezentowana. W przypadku i3 podzielenie przez 2 przenosi niepodpisaną wartość z powrotem do zakresu int, a więc kończymy na dużej wartości int po konwersji.

Odpowiednie sekcje z projektu standardowego C++ są następujące. Sekcja 5.7[expr.add]:

operatorów dodatków + i - grupa od lewej do prawej. Typowe konwersje arytmetyczne są wykonywane dla argumentów typu arytmetycznego lub wyliczeniowego.

Zwykłe arytmetyczne konwersje są ujęte w sekcji 5 i mówi:

wielu operatorów binarnych, które oczekują argumentów arytmetyki lub wyliczenie typu konwersji przyczyna i dają rodzaje wynikowych w podobny sposób. Celem jest uzyskanie wspólnego typu, który jest także typem wyniku. Ten wzór nazywa zwykłe konwersje arytmetyczne, które są zdefiniowane w następujący sposób:

[...]

  • W przeciwnym razie, jeśli operand że ma całkowitą bez znaku typu ma rangi większa lub równa pozycja typu drugiego operandu, operand z typem liczby całkowitej ze znakiem jest konwertowany na typ argumentu z bez znaku typu całkowitoliczbowego.

i konwersji z wartości, która nie może być reprezentowane przez podpisany typu punkt 4.7[conv.integral]:

Jeżeli typ docelowy jest podpisany wartość pozostaje niezmieniony, jeśli może być reprezentowany w typie miejsca docelowego (i szerokość pola bitowego); w przeciwnym razie wartość jest definiowana przez implementację.

liczb całkowitych bez znaku i wypełnia przekrój modulo arytmetyczną 3.9.1[basic.fundamental]:

liczby całkowite powinien stosować prawa arytmetyki modulo 2n, gdzie n oznacza liczbę bitów wartości reprezentacja tej konkretnej wielkości integer.48

+0

@Hurkyl: Cholera, śpię dziś stojąc, przekręciłem niepodpisany przelew i konwersję z podpisu bez podpisu (ten drugi to implementacja zdefiniowana). Ja sam zniszczę mój komentarz ... –

Powiązane problemy