2015-07-27 19 views
10

Mam dość podstawowe pytanie, ale nie jestem pewien, czy rozumiem tę koncepcję, czy nie. Załóżmy, że mamy:Mnożenie dwóch liczb całkowitych w C++

int a = 1000000; 
int b = 1000000; 
long long c = a * b; 

Kiedy uruchamiam to, c pokazuje wartość ujemną, więc zmieniłem również aib na długi, długi i wtedy wszystko było w porządku. Dlaczego więc muszę zmienić aib, gdy ich wartości są w zakresie int, a ich produkt jest przypisany do c (która jest długa)?

Używam C/C++

+17

w ints nie są promowane na długo na długo przed mnożeniem, oni pozostaną także ints i produkt. Następnie produkt jest rzucany na długo, ale zbyt późno, nadszedł uderzenie. Posiadanie jednego z a lub b long long powinno również zadziałać, podobnie jak inne będą promowane. –

+0

Powinieneś oznaczyć język programowania, którego używasz, ponieważ różne języki mogą wprowadzać inne zachowanie;) – alfasin

+0

W zależności od tego, która maszyna i wersja C, której używasz, ma rozmiar "int", może również ulec zmianie. – Jay

Odpowiedz

18

W int s nie są promowane na long long przed mnożeniem, pozostają one int S i produktu, jak również. Następnie produkt jest przesyłany do long long, ale zbyt późno, nastąpiło przepełnienie.

Posiadanie jednego z a lub blong long powinno również działać, podobnie jak inne będą promowane.

2

Dla operatorów arytmetycznych typ wyniku nie jest zależny od tego, do czego przypisujesz wynik, ale do typów operandów. Dla operatorów arytmetycznych wykonywane są operandy na usual arithmetic conversions. Ta funkcja służy do doprowadzenia argumentów do wspólnego typu, oznacza to dla typów mniejszy niż niepodpisanych/podpisanych int jeśli wartości zmieści są promowane na unsigned/podpisał int, w tym przypadku są one już zarówno int, więc nie wymaga konwersji. Zobacz Why must a short be converted to an int before arithmetic operations in C and C++?, aby dowiedzieć się, dlaczego.

Co mamy teraz jest niezdefiniowane zachowanie ponieważ podpisał całkowitą przepełnienie jest niezdefiniowane zachowanie, jest to uwzględnione w projekcie C++ standardowej sekcji 5[wyrażenie] który mówi:

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined. [ Note: most existing implementations of C++ ignore integer overflows. Treatment of division by zero, forming a remainder using a zero divisor, and all floating point exceptions vary among machines, and is usually adjustable by a library function. —end note ]

Teraz-dni mamy mają odkażających do połowu tego typu zachowanie niezdefiniowane i korzystania -fsanitize=undefined zarówno z brzękiem i gcc złapie to na starcie z powodu następującego błędu (see it live):

runtime error: signed integer overflow: 1000000 * 1000000 cannot be represented in type 'int'

Dla sekcji odniesienia 5.6[wyr.mul] mówi:

[...]The usual arithmetic conversions are performed on the operands and determine the type of the result.

i sekcja 5 mówi:

Otherwise, the integral promotions (4.5) shall be performed on both operands.61 Then the following rules shall be applied to the promoted operands

  • If both operands have the same type, no further conversion is needed.
0

To trochę absurdalne, ponieważ instrukcja asemblera nie zawsze obliczyć

int * int -> 64 bitów

, więc jeśli spojrzysz na kod maszynowy, zobaczysz: imul które przechowują 64bits do eax EDX następnie CDQ że umieścić znak bitowej eax do EDX (tracąc w ten sposób pełny rezultat 64-bitowego) a następnie eax EDX są przechowywane w zmiennej 64Bits

a jeśli konwersji wartości 32-bitowego na 64bits przed mnożeniem, masz wywołanie funkcji 64Bits mnożenia bez powodu

(sprawdziłem: to nie jest przypadek, gdy kod jest zoptymalizowany)

Powiązane problemy