2012-05-02 67 views
8

Tại sao hai thao tác sau mang lại kết quả khác nhau trong Java cho x = 31 hoặc 32 nhưng kết quả tương tự cho x=3?Kết quả Java khác nhau đối với (int) Math.pow (2, x) và 1 << x

int x=3; 
int b = (int) Math.pow(2,x); 
int c = 1<<x; 

Kết quả:

x=32: b=2147483647; c=1; 
x=31: b=2147483647; c=-2147483648; 
x=3: b=8   ; c=8 
+1

Sự khác biệt tinh tế là pow() là ** nhiều ** chậm hơn ngay cả khi câu trả lời giống nhau. pow() có lỗi làm tròn, trong khi 'int' có tràn. Bạn có thể thử '1L << 32' bằng' 2147483648' –

Trả lời

18

Có nhiều vấn đề tại chơi:

gì câu hỏi phỏng vấn này không có gì cho thấy (int)Math.pow(2, x)1 << x là không tương đương với giá trị của x bên ngoài phạm vi 0 ... 30.

P.S. Có lẽ điều thú vị cần lưu ý là sử dụng long thay cho int (và 1L thay cho số 1) sẽ đưa ra một tập hợp kết quả khác với hai kết quả kia. Điều này giữ ngay cả khi kết quả cuối cùng được chuyển đổi thành int.

+2

Đặc biệt điểm cuối cùng là rất quan trọng. –

2

Hãy xem xét các giới hạn của kiểu int. Làm thế nào một số lượng lớn nó có thể giữ?

+0

Câu hỏi đặt ra là sự khác biệt giữa sự dịch chuyển trái và quyền hạn rõ ràng của 2. –

+1

Và thêm vào giới hạn 'int' thực tế là đó là một kiểu đã ký. –

3

Theo tài liệu Math.pow sẽ quảng bá cả hai đối số của nó để tăng gấp đôi và trả về gấp đôi. Rõ ràng khi kết quả trả về là gấp đôi và bạn cast nó vào int bạn sẽ chỉ nhận được 32 bit cao nhất và phần còn lại sẽ bị cắt bớt - do đó bạn luôn nhận được giá trị (int) Math.pow(2,x);. Khi bạn làm bithift bạn luôn làm việc với ints và do đó một tràn xảy ra.

+0

Tôi đoán số bit cao nhất là 32 bit. Tôi có thể bị nhầm lẫn. – asenovm

+0

Hm, được rồi, điều đó có thể đúng. Tôi chỉ nhớ nó luôn luôn trả về '2^31-1'. Xóa nhận xét :) –

0

int có kích thước 32 bit và vì nó được ký (theo mặc định), bit đầu tiên được sử dụng cho dấu. Khi bạn dịch chuyển 31 bit còn lại, bạn sẽ nhận được Two's Compliment, tức là - (2^32). Khi bạn chuyển sang trái 32 bit, nó chỉ lặp lại tất cả các con đường xung quanh 1. Nếu bạn đã làm điều này chuyển với longs thay vì ints, bạn sẽ nhận được câu trả lời bạn mong đợi (đó là cho đến khi bạn thay đổi 63 + bit).

0

Đây là điểm chuẩn vi mô trong trường hợp dài. Trên máy tính xách tay của tôi (2.8GHz), sử dụng shift thay vì Math.pow nhanh hơn 7 lần.

int limit = 50_000_000; 
@Test 
public void testPower() { 
    Random r = new Random(7); 
    long t = System.currentTimeMillis(); 
    for (int i = 0; i < limit; i++) { 
     int p = r.nextInt(63); 
     long l = (long)Math.pow(2,p); 
    } 
    long t1 = System.currentTimeMillis(); 
    System.out.println((t1-t)/1000.0); // 3.758 s 
} 
@Test 
public void testShift() { 
    Random r = new Random(7); 
    long t = System.currentTimeMillis(); 
    for (int i = 0; i < limit; i++) { 
     int p = r.nextInt(63); 
     long l = 1L << p; 
    } 
    long t1 = System.currentTimeMillis(); 
    System.out.println((t1-t)/1000.0); // 0.523 s 
} 
Các vấn đề liên quan