2015-07-27 88 views
10

Tôi có một câu hỏi khá cơ bản, nhưng tôi không chắc liệu tôi có hiểu khái niệm này hay không. Giả sử chúng ta có:Nhân hai số nguyên trong C++

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

Khi tôi chạy điều này, c cho thấy giá trị âm, vì vậy tôi cũng thay đổi a và b thành dài và sau đó mọi thứ đều ổn. Vậy tại sao tôi phải thay đổi a và b, khi giá trị của chúng nằm trong phạm vi int và sản phẩm của chúng được gán cho c (dài và dài)?

Tôi đang sử dụng C/C++

+17

các ints không thăng long trước khi nhân lên, họ vẫn còn ints và sản phẩm là tốt. Sau đó, sản phẩm được đúc dài, nhưng quá muộn, tràn đã xảy ra. Có một hoặc một lâu dài cũng nên hoạt động, vì cái kia sẽ được quảng bá. –

+0

Bạn nên gắn thẻ ngôn ngữ lập trình bạn đang sử dụng vì các ngôn ngữ khác nhau có thể giới thiệu các hành vi khác nhau;) – alfasin

+0

Tùy thuộc vào máy và phiên bản C bạn đang sử dụng kích thước của 'int' có thể thay đổi. – Jay

Trả lời

18

Các int s không thăng long long trước nhân, họ vẫn int s và sản phẩm là tốt. Sau đó, sản phẩm được đúc thành long long, nhưng quá muộn, tràn đã xảy ra.

Có một trong số a hoặc blong long cũng sẽ hoạt động, vì quảng cáo còn lại sẽ được quảng cáo.

2

Đối với toán tử số học, loại kết quả không phụ thuộc vào những gì bạn đang gán kết quả cho nhưng loại toán hạng. Đối với các toán tử số học, usual arithmetic conversions được thực hiện trên các toán hạng. Này được sử dụng để mang lại các toán hạng để một loại phổ biến, điều này có nghĩa với nhiều loại nhỏ hơn unsigned/ int nếu các giá trị có thể phù hợp họ đang thăng unsigned/ int, trong trường hợp này họ đã cả int nên không cần chuyển đổi. Xem Why must a short be converted to an int before arithmetic operations in C and C++? để biết chi tiết về lý do.

Những gì chúng ta có bây giờ là hành vi không xác định kể từ khi ký hợp tràn số nguyên là hành vi không xác định, điều này được đề cập trong dự thảo C++ phần tiêu chuẩn 5[expr] mà nói:

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 ]

Bây giờ, một ngày chúng tôi có vệ sinh để đón các loại hành vi undefined và sử dụng -fsanitize=undefined với cả hai kêu vang và gcc sẽ bắt này khi chạy với các lỗi sau (see it live):

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

Để tham khảo phần 5.6[expr.mul] nói:

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

và phần 5 nói:

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

Đó là loại ngớ ngẩn, bởi vì các hướng dẫn lắp ráp không luôn luôn tính toán

int * int -> 64 bit dài

vì vậy, nếu bạn nhìn vào mã máy, bạn sẽ thấy: imul lưu trữ 64bits vào eax edx sau đó CDQ mà đặt dấu chút eax vào edx (do đó mất kết quả 64bits đầy đủ) và sau đó eax edx được lưu trữ vào 64bits biến

và nếu bạn chuyển đổi các giá trị 32bits vào 64bits trước nhân, bạn nhận được một cuộc gọi đến chức năng 64bits nhân không có lý do

(tôi đã kiểm tra: nó không phải là trường hợp khi mã được tối ưu hóa)