2012-04-02 43 views
5

Khi tiêu đề câu hỏi đọc, gán 2^31 cho biến số nguyên 32 bit đã ký và chưa ký cho kết quả không mong muốn.Kết quả lạ sau khi gán 2^31 cho biến số nguyên 32 bit đã ký và chưa ký

Dưới đây là chương trình ngắn (trong C++), mà tôi thực hiện để xem những gì đang xảy ra:

#include <cstdio> 
using namespace std; 

int main() 
{ 
    unsigned long long n = 1<<31; 
    long long n2 = 1<<31; // this works as expected 
    printf("%llu\n",n); 
    printf("%lld\n",n2); 
    printf("size of ULL: %d, size of LL: %d\n", sizeof(unsigned long long), sizeof(long long)); 
    return 0; 
} 

Dưới đây là kết quả:

MyPC/# c++ test.cpp -o test 
MyPC/# ./test 
18446744071562067968  <- Should be 2^31 right? 
-2147483648    <- This is correct (-2^31 because of the sign bit) 
size of ULL: 8, size of LL: 8 

sau đó tôi thêm một chức năng p(), để nó :

void p() 
{ 
    unsigned long long n = 1<<32; // since n is 8 bytes, this should be legal for any integer from 32 to 63 
    printf("%llu\n",n); 
} 

Khi biên dịch và chạy, đây là điều gây nhầm lẫn d tôi thậm chí nhiều hơn:

MyPC/# c++ test.cpp -o test 
test.cpp: In function ‘void p()’: 
test.cpp:6:28: warning: left shift count >= width of type [enabled by default] 
MyPC/# ./test 
0 
MyPC/

Tại sao trình biên dịch phàn nàn về số lần dịch chuyển trái quá lớn? sizeof(unsigned long long) trả về 8, vậy điều đó có nghĩa là 2^63-1 là giá trị tối đa cho loại dữ liệu đó không?

Nó đánh tôi rằng có lẽ n * 2 và n < < 1, không phải lúc nào cư xử theo cách tương tự, vì vậy tôi cố gắng này:

void s() 
{ 
    unsigned long long n = 1; 
    for(int a=0;a<63;a++) n = n*2; 
    printf("%llu\n",n); 
} 

này cung cấp cho các giá trị chính xác của 2^63 như đầu ra là 9223372036854775808 (Tôi đã xác minh nó bằng python). Nhưng những gì là sai trái khi làm một shit trái?

Một sự thay đổi số học trái của n là tương đương với nhân với 2 n (với điều kiện giá trị không tràn)

- Wikipedia

Giá trị không được tràn , chỉ một dấu trừ sẽ xuất hiện vì giá trị là 2^63 (tất cả các bit được đặt).

Tôi vẫn không thể tìm ra những gì đang xảy ra với sự dịch chuyển trái, bất kỳ ai cũng có thể giải thích điều này không?

PS: Chương trình này được chạy trên một bạc hà 32-bit hệ thống chạy Linux (nếu điều đó giúp)

+0

này nên được 'unsigned dài dài n = 1ULL << 31;' – kirilloid

+0

thần! nó có dễ dàng không ?! Tại sao tôi không nghĩ về nó. anyways, có 1ULL << 31 không hoạt động. cảm ơn! – Rushil

Trả lời

10

On line này:

unsigned long long n = 1<<32; 

Vấn đề là các literal 1 là loại int - có lẽ chỉ 32 bit. Do đó sự thay đổi sẽ đẩy nó ra khỏi giới hạn.

Chỉ vì bạn đang lưu trữ vào một kiểu dữ liệu lớn hơn không có nghĩa là mọi thứ trong biểu thức được thực hiện ở kích thước lớn hơn.

Vì vậy, để sửa chữa nó, bạn cần phải hoặc cast nó lên hoặc làm cho nó một nghĩa đen unsigned long long:

unsigned long long n = (unsigned long long)1 << 32; 
unsigned long long n = 1ULL << 32; 
+0

Liên quan đến đề xuất cuối cùng: ** xin vui lòng ** sử dụng mũ cho các nhà thiết kế loại. Với nhiều phông chữ, phân biệt giữa '1ll' và' 111' có thể khó, nếu không phải là không thể; '1LL' rõ ràng và không rõ ràng (và không có hậu tố' O' để tạo ra các vấn đề với '0'). –

+0

Đề xuất được thực hiện. :) – Mysticial

5

Lý do 1 << 32 thất bại là bởi vì 1 không có đúng loại (nó là int).Trình biên dịch không thực hiện bất kỳ phép chuyển đổi nào trước khi phép gán thực sự xảy ra, do đó, 1 << 32 được đánh giá bằng cách sử dụng int arithmic, đưa ra cảnh báo về tràn.

Hãy thử sử dụng 1LL hoặc 1ULL thay vào đó tương ứng có loại long longunsigned long long.

3

Dòng

unsigned long long n = 1<<32; 

kết quả trong một tràn, vì theo nghĩa đen 1 là loại int, vì vậy 1 << 32 cũng là một int, mà là 32 bit trong hầu hết các trường hợp.

Dòng

unsigned long long n = 1<<31; 

cũng tràn, với lý do tương tự. Lưu ý rằng 1 là loại signed int, do đó, nó thực sự chỉ có 31 bit cho giá trị và 1 bit cho dấu. Vì vậy, khi bạn thay đổi 1 << 31, nó sẽ tràn các bit giá trị, dẫn đến -2147483648, sau đó được chuyển đổi thành số không dài chưa ký, là 18446744071562067968. Bạn có thể xác minh điều này trong trình gỡ rối, nếu bạn kiểm tra các biến và chuyển đổi chúng.

Vì vậy, sử dụng

unsigned long long n = 1ULL << 31; 
Các vấn đề liên quan