2010-03-04 30 views
5

Khi tôi chạy cùng mã chính xác thực hiện cùng một phép tính dấu chấm động (sử dụng tăng gấp đôi) được biên dịch trên Windows và Solaris, tôi nhận được kết quả hơi khác.Thay đổi tính toán dấu chấm động phụ thuộc vào trình biên dịch

Tôi biết rằng kết quả không chính xác do lỗi làm tròn. Tuy nhiên tôi đã có thể mong đợi các lỗi làm tròn được nền tảng độc lập, do đó cho là kết quả tương tự (hơi không chính xác) trên cả hai nền tảng, mà không phải là trường hợp.

Điều này có bình thường không, hoặc tôi có vấn đề gì khác trong mã của mình không?

+2

Nó sẽ giúp đỡ nếu bạn gửi một trường hợp thử nghiệm mã của bạn. Đối với một vấn đề như vậy, trường hợp thử nghiệm không được dài quá 3-4 dòng. – Sparr

+1

Bản sao có thể có của [Tại sao cùng một mã sẽ mang lại kết quả số khác nhau trên 32 so với các máy 64 bit?] (Http://stackoverflow.com/questions/7847274/why-would-the-same-code-yield-different- số-kết quả-trên-32-vs-64-bit-máy) –

Trả lời

6

Trên x86, thường hầu hết các phép tính xảy ra với số lượng 80 bit, trừ khi bị buộc phải có độ chính xác gấp đôi. Hầu hết các kiến ​​trúc khác mà tôi biết đều thực hiện tất cả các phép tính với độ chính xác gấp đôi (một lần nữa, trừ khi được ghi đè khác).

Tôi không biết nếu bạn đang chạy Solaris trên SPARC hoặc x86, nhưng nếu trước đây, sau đó tôi rất nghi ngờ rằng đó là nguyên nhân của sự khác biệt.

+1

Nếu đây là Solaris x86, biểu diễn 80 bit này vẫn có thể là nguyên nhân của sự khác biệt. Các tính toán dấu chấm động trên x86 chỉ xảy ra với số lượng 80 bit nếu nó sử dụng bộ xử lý đối xứng x87 (có sẵn); nếu nó sử dụng đơn vị vectơ SSE mới hơn, chúng là đôi gấp đôi bình thường 64 bit. GCC sẽ có xu hướng theo mặc định để sử dụng x87 để tương thích với các bộ vi xử lý cũ; bất kỳ trình biên dịch nào khác sẽ sử dụng đơn vị SSE vì nó nhanh hơn ngay cả đối với những thứ không phải là vectơ. –

+0

@Brooks: Cảm ơn! Rất rõ ràng. –

+3

Bạn được chào đón! Nhân tiện, http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323 là một cuộc thảo luận khá đầy đủ về điều này. (Nó cũng là kết quả đầu tiên của Google cho 'lỗi điểm động gcc', sẽ cho bạn biết tần suất mọi người gặp phải vấn đề này!) –

1

Chủ đề của câu hỏi của bạn cho thấy rằng nó có thể phụ thuộc vào trình biên dịch. Nó có thể, nhưng thực tế là bạn đang chạy trên phần cứng khác nhau (giả sử Solaris của bạn không phải là x86) cho thấy một lý do nhiều khả năng hơn cho sự khác biệt - sự khác biệt trong phần cứng chính nó.

Nền tảng phần cứng khác nhau có thể sử dụng các thiết bị phần cứng hoàn toàn khác nhau (FPU, CPU) để thực hiện các phép tính dấu phẩy động, đến các kết quả khác nhau.

Hơn nữa, thường các đơn vị FPU có thể được cấu hình bởi một số cài đặt liên tục, như mô hình vô cùng, chế độ làm tròn, v.v. Phần cứng khác có thể có thiết lập mặc định khác. Trình biên dịch thông thường sẽ tạo ra mã sẽ khởi tạo FPU khi khởi động chương trình, bởi thiết lập ban đầu đó cũng có thể khác nhau. Cuối cùng, các triển khai khác nhau của ngôn ngữ C++ có thể thực hiện ngữ nghĩa dấu phẩy khác nhau, vì vậy bạn thậm chí có thể nhận được kết quả khác nhau từ các trình biên dịch C++ khác nhau của cùng một phần cứng.

2

Tôi tin rằng trong Windows/x86, mã của bạn sẽ chạy với độ chính xác x87 đã được đặt thành 53 bit (độ chính xác gấp đôi), mặc dù tôi không chắc chắn chính xác thời điểm này được đặt. Trên Solaris/x86, FPU x87 có khả năng sử dụng độ chính xác mặc định là 64 bit (độ chính xác mở rộng), do đó có sự khác biệt. Có một kiểm tra đơn giản bạn có thể làm để phát hiện độ chính xác (53 bit hoặc 64 bit) đang được sử dụng: thử tính toán một cái gì đó như 1e16 + 2.9999, trong khi cẩn thận tránh tối ưu hóa trình biên dịch gấp liên tục (ví dụ: xác định hàm add riêng biệt để thực hiện việc bổ sung và tắt mọi tối ưu hóa có thể có chức năng nội tuyến). Khi sử dụng độ chính xác 53 bit (SSE2, hoặc x87 ở chế độ hai độ chính xác), điều này cho phép 1e16 + 2; khi sử dụng độ chính xác 64 bit (x87 trong chế độ chính xác mở rộng), điều này cho phép 1e16 + 4. Kết quả thứ hai đến từ một hiệu ứng gọi là 'làm tròn đôi', kết quả của phép cộng được làm tròn đầu tiên thành 64 bit, và sau đó đến 53 bit . (Thực hiện phép tính này trực tiếp bằng Python, tôi nhận được 1e16 + 4 trên Linux 32 bit và 1e16 + 2 trên Windows, cho cùng một lý do chính xác.)

Đây là một bài viết rất hay (vượt quá đáng kể) trích dẫn của Goldberg "gì mỗi nhà khoa học máy tính nên biết ...") giải thích một số vấn đề phát sinh từ việc sử dụng các FPU x87:

http://hal.archives-ouvertes.fr/docs/00/28/14/29/PDF/floating-point-article.pdf

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