Jak działają zasady promocji, gdy znaki po obu stronach operatora binarnego różnią się? [duplikat]

To pytanie ma już odpowiedź tutaj:

Rozważ następujące programy:

// http://ideone.com/4I0dT
#include <limits>
#include <iostream>

int main()
{
    int max = std::numeric_limits<int>::max();
    unsigned int one = 1;
    unsigned int result = max + one;
    std::cout << result;
}

I

// http://ideone.com/UBuFZ
#include <limits>
#include <iostream>

int main()
{
    unsigned int us = 42;
    int neg = -43;
    int result = us + neg;
    std::cout << result;
}

Skąd operator + "wie", który typ jest prawidłowy do zwrócenia? Ogólną zasadą jest konwersja wszystkich argumentów na najszerszy Typ, ale tutaj nie ma wyraźnego "zwycięzcy" między int a unsigned int. W pierwszym przypadku, {[3] } musi być wybrany jako wynik operator+, ponieważ otrzymuję wynik 2147483648. W drugim przypadku należy wybrać int, ponieważ otrzymuję wynik -1. Jednak nie widzę w ogólnym przypadku, Jak to jest decydujące. Czy to nieokreślone zachowanie, które widzę, czy coś innego?

Author: Wolf, 2011-07-21

3 answers

Jest to wyraźnie opisane w §5/9:

Wiele operatorów binarnych, które oczekują operandów typu arytmetycznego lub wyliczeniowego, powoduje konwersje i daje typy wyników w podobny sposób. Celem jest uzyskanie wspólnego typu, który jest również typem wyniku. Wzór ten nazywa się zwykłymi przekształceniami arytmetycznymi , które są zdefiniowane w następujący sposób:

  • jeśli jeden z operandów jest typu long double, drugi konwertuje się na long double.
  • W przeciwnym razie, jeśli jeden z operandów jest double, drugi konwertuje się na double.
  • W przeciwnym razie, jeśli jeden z operandów jest float, drugi konwertuje się na float.
  • W Przeciwnym Razie całkowe promocje będą wykonywane na obu operandach.
  • wtedy, jeśli jeden z operandów jest unsigned long, drugi jest konwertowany na unsigned long.
  • W przeciwnym razie, jeśli jeden operand jest long int, a drugi unsigned int, to jeśli long int może reprezentować wszystkie wartości unsigned int, to unsigned int konwertuje się na long int; w przeciwnym razie oba operandy należy konwertować na unsigned long int.
  • W przeciwnym razie, jeśli jeden z operandów jest long, drugi konwertuje się na long.
  • W przeciwnym razie, jeśli jeden z operandów jest unsigned, drugi konwertuje się na unsigned.

[Uwaga: w przeciwnym razie jedynym pozostałym przypadkiem jest to, że oba operandy to int]

W obu Twoich scenariuszach wynikiem operator+ jest unsigned. W związku z tym drugi scenariusz jest skutecznie:

int result = static_cast<int>(us + static_cast<unsigned>(neg));

Ponieważ w tym przypadku wartość us + neg nie jest reprezentowalna przez int, wartość result jest zdefiniowana w implementacji - §4.7 / 3:

Jeśli typ docelowy jest podpisany, wartość nie zmienia się, jeśli może być reprezentowana w typie docelowym (i szerokości pola bitowego); w przeciwnym razie wartość jest zdefiniowana w implementacji.

 34
Author: ildjarn,
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
2012-10-15 04:33:06

Zanim C został ustandaryzowany, istniały różnice między kompilatorami-niektóre stosowały reguły "zachowania wartości", a inne reguły" zachowania znaków". Zachowanie znaku oznaczało, że jeśli któryś z operandów był niepodpisany, wynik był niepodpisany. Było to proste, ale czasami dało dość zaskakujące wyniki(zwłaszcza gdy liczba ujemna została przekonwertowana na niepodpisaną).

C ustandaryzowany na raczej bardziej złożonych regułach "zachowywania wartości". Zgodnie z zasadami zachowania wartości, promocja can / does zależy od rzeczywistych zakresów typów, dzięki czemu można uzyskać różne wyniki na różnych kompilatorach. Na przykład, w większości kompilatorów MS-DOS, int jest tego samego rozmiaru co short i long różni się od obu. W wielu systemach int jest tej samej wielkości co long, a short różni się od obu. Z regułami zachowania wartości, mogą one prowadzić do tego, że promowany typ różni się między nimi.

Podstawową ideą zasad zachowania wartości jest to, że będzie promować do większej Typ signed, jeśli może reprezentować wszystkie wartości typu smaller. Na przykład 16-bitowy unsigned short może być promowany do 32-bitowego signed int, ponieważ każda możliwa wartość unsigned short może być reprezentowana jako signed int. Typy będą promowane do typu unsigned wtedy i tylko wtedy, gdy jest to konieczne do zachowania wartości mniejszego typu (np. jeśli unsigned short i signed int są 16 bitami, to signed int nie może reprezentować wszystkich możliwych wartości unsigned short, więc unsigned short będzie promowany do unsigned int).

Kiedy przypisujesz wynik jak masz, wynik zostanie przekonwertowany na typ docelowy i tak, więc większość z tego robi stosunkowo niewielką różnicę - przynajmniej w większości typowych przypadków, gdzie po prostu skopiuje bity do wyniku i to do ciebie należy decyzja, czy zinterpretować to jako Podpisane lub niepodpisane.

Kiedy nie przypisujesz wyniku, takiego jak w porównaniu, rzeczy mogą być dość brzydkie. Na przykład:

unsigned int a = 5;
signed int b = -5;

if (a > b)
    printf("Of course");
else
    printf("What!");

Zgodnie z zasadami zachowania znaków, b byłoby promowane do unsigned, a w procesie stają się równe UINT_MAX - 4, więc " co!"noga if zostanie zabrana. Z regułami zachowania wartości, można uda się uzyskać jakieś dziwne wyniki trochę jak to, ale 1) głównie na systemach DOS-podobnych, gdzie int jest tego samego rozmiaru co short, i 2) ogólnie jest to trudne do zrobienia i tak.

 11
Author: Jerry Coffin,
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
2011-07-21 01:24:45

To wybór typu, do którego wkładasz wynik, lub przynajmniej cout honoruje ten typ podczas wyjścia.

Nie pamiętam na pewno, ale myślę, że kompilatory C++ generują ten sam kod arytmetyczny dla obu, to tylko porównywanie i wyjście, które dbają o znak.

 2
Author: Andrew White,
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
2011-07-21 01:19:35