2012-04-17 39 views
18

Tại sao điều này đúng? Java xuất hiện để tạo ra một kết quả với một sự khác biệt nhỏ khi nhân hai float so với C và thậm chí là phương thức Java Math.pow.Điểm nổi bật Java vs C: "x * x" khác với "pow (x, 2)"?

Java:

float a = 0.88276923; 

double b = a * a; // b becomes 0.779281497001648 <---- what??? 
b = Math.pow(a,2); // b becomes 0.7792815081874238 

C:

float a = 0.88276923; 

double b = a * a; // b becomes 0.7792815081874238 
pow(a,2);   // b becomes 0.7792815081874238 

Cập nhật: mỗi bình luận Ed S., tôi cũng đã tìm thấy rằng các hành vi C thay đổi tùy thuộc vào trình biên dịch. Sử dụng gcc nó xuất hiện để phù hợp với hành vi Java. Sử dụng studio trực quan (tùy thuộc vào nền tảng đích của bạn) nó có thể tạo ra các kết quả được thấy ở trên hoặc những kết quả được thấy trong Java. Ugh.

+2

http://floating-point-gui.de/ –

+3

Ah, số học dấu chấm động. Một pháo đài thuần khiết về độ chính xác và độ tin cậy. – Perception

+1

Tôi biết rằng phao không chính xác. Tuy nhiên, tôi cho rằng sự thiếu chính xác của họ là nhất quán. – mark

Trả lời

16

Như pst và sự thật đã lưu ý một cách khôn ngoan, C đang quảng cáo float thành doubletrước khi nhân. Trên thực tế, chúng được quảng cáo với giá trị độ chính xác mở rộng 80 bit khi chúng được đẩy lên ngăn xếp. Đây là kết quả lắp ráp (VS2005 x86 C89)

double b = a * a; 
00411397 fld   dword ptr [a] 
0041139A fmul  dword ptr [a] 
0041139D fstp  qword ptr [b] 

Các FLD Hướng dẫn

Các hướng dẫn FLD tải một 32 bit, 64 bit, hoặc 80 bit giá trị dấu chấm vào stack. Lệnh này chuyển đổi các toán hạng 32 và 64 bit thành giá trị chính xác mở rộng 80 bit trước khi đẩy giá trị vào ngăn xếp dấu chấm động.


Điều thú vị là, nếu tôi xây dựng để nhắm mục tiêu x64, hướng dẫn movss được sử dụng và bạn nhận được một giá trị của 0.779281497001648 như kết quả, ví dụ, những gì bạn đang nhìn thấy trong ví dụ java của bạn. Hãy thử một lần.

+2

+1. Bây giờ, ở đâu trong tiêu chuẩn C (và đừng quên chỉ định bản nháp nào!) Là có một câu lệnh vauge có thể được hiểu là hành vi được xác định thực hiện trên hai .... ;-) –

+2

@pst: Tôi ước gì mình có thời gian để xem, nhưng .... đó là ngày đêm: D –

+0

@pst: Đã thử nó nhắm mục tiêu x64 ra khỏi tò mò và bạn thấy hành vi java như lệnh 'fld' không còn được sử dụng (trong thiết lập của tôi ít nhất). –

10

gì Java làm cho

double b = a * a; 

là nhân a * a như một (32-bit) float đầu tiên, và chuyển đổi kết quả vào một (64-bit) double khi giao đến b.

b = Math.pow(a,2); 

Chuyển đổi a đến một (64-bit) double đầu tiên (kể từ khi các tham số cho Math.powdouble, double) và sau đó hình vuông nó.

gì là khó hiểu (với tôi) là lý do tại sao C dường như cast a 's để double đầu tiên trong

double b = a * a; 

Có phải đó là trong tiêu chuẩn?

Edit: Tôi lờ mờ nhớ về C không đòi hỏi một việc thực hiện cụ thể (về bao nhiêu bit được sử dụng) cho số ... là những gì đang xảy ra ở đây? Có phải của bạn là float s 64 bit không? (Trong Java, float luôn là 32 bit và double luôn là 64 bit).

Chỉnh sửa: Cả câu trả lời và đánh dấu của Ed S. là các trình biên dịch khác nhau cho kết quả khác nhau cho thấy kết quả C là triển khai và kiến ​​trúc cụ thể.

+0

Tôi tự hỏi nếu có bất kỳ đánh giá thời gian biên dịch (tối ưu hóa) xảy ra là tốt? –

+0

(Mặc dù tôi nghi ngờ rằng 'sizeof (float) == sizeof (double)' trên một máy/biên dịch hiện đại ;-) –

+0

Trên máy của tôi, chúng được quảng cáo khi được đẩy lên ngăn xếp. Xem: [Hướng dẫn FLD] (http://webster.cs.ucr.edu/AoA/Windows/HTML/RealArithmetica2.html) –

Các vấn đề liên quan