2015-07-14 13 views
9

Tôi biết tất cả về các vấn đề gần đúng với số dấu phẩy động vì vậy tôi hiểu cách 4.5 có thể được làm tròn xuống 4 nếu nó xấp xỉ là 4.4999999999999991. Câu hỏi của tôi là tại sao có sự khác biệt khi sử dụng cùng loại với 32 bit và 64 bit.Sự khác biệt điểm nổi giữa 64 bit và 32 bit với Vòng

Trong mã bên dưới, tôi có hai phép tính. Trong 32 bit giá trị cho MyRoundValue1 là 4 và giá trị cho MyRoundValue2 là 5. Trong 64 bit chúng đều là 4. Không nên kết quả phù hợp với cả 32 bit và 64 bit?

{$APPTYPE CONSOLE} 
const 
    MYVALUE1: Double = 4.5; 
    MYVALUE2: Double = 5; 
    MyCalc: Double = 0.9; 
var 
    MyRoundValue1: Integer; 
    MyRoundValue2: Integer; 
begin 
    MyRoundValue1 := Round(MYVALUE1); 
    MyRoundValue2 := Round(MYVALUE2 * MyCalc); 
    WriteLn(IntToStr(MyRoundValue1)); 
    WriteLn(IntToStr(MyRoundValue2)); 
end. 

Trả lời

7

Trong x87 mã này:

MyRoundValue2 := Round(MYVALUE2 * MyCalc); 

được biên dịch để:

 
MyRoundValue2 := Round(MYVALUE2 * MyCalc); 
0041C4B2 DD0508E64100  fld qword ptr [$0041e608] 
0041C4B8 DC0D10E64100  fmul qword ptr [$0041e610] 
0041C4BE E8097DFEFF  call @ROUND 
0041C4C3 A3C03E4200  mov [$00423ec0],eax 

Từ kiểm soát mặc định cho các đơn vị x87 dưới Delphi RTL thực hiện các tính toán đến 80 bit chính xác. Vì vậy, đơn vị dấu chấm động nhân 5 với số closest 64 bit value to 0.9 là:

 
0.90000 00000 00000 02220 44604 92503 13080 84726 33361 81640 625 

Lưu ý rằng giá trị này lớn hơn 0,9. Và nó chỉ ra rằng khi nhân với 5, và làm tròn tới giá trị 80 bit gần nhất, giá trị lớn hơn 4.5. Do đó Round(MYVALUE2 * MyCalc) trả về 5.

Trên 64 bit, phép tính điểm động được thực hiện trên đơn vị SSE. Điều đó không sử dụng các giá trị trung gian 80 bit. Và nó chỉ ra rằng 5 lần gần gấp đôi đến 0,9, được làm tròn đến độ chính xác gấp đôi là chính xác 4,5. Do đó Round(MYVALUE2 * MyCalc) trả về 4 trên 64 bit.

Bạn có thể thuyết phục các trình biên dịch 32 bit để hành xử theo cách tương tự như trình biên dịch 64 bit bằng cách lưu trữ vào một đôi hơn là dựa trên các giá trị 80 bit trung gian:

{$APPTYPE CONSOLE} 
const 
    MYVALUE1: Double = 4.5; 
    MYVALUE2: Double = 5; 
    MyCalc: Double = 0.9; 
var 
    MyRoundValue1: Integer; 
    MyRoundValue2: Integer; 
    d: Double; 
begin 
    MyRoundValue1 := Round(MYVALUE1); 
    d := MYVALUE2 * MyCalc; 
    MyRoundValue2 := Round(d); 
    WriteLn(MyRoundValue1); 
    WriteLn(MyRoundValue2); 
end. 

Chương trình này sẽ cho kết quả tương tự như của bạn Chương trình 64 bit.

Hoặc bạn có thể buộc đơn vị x87 sử dụng trung gian 64 bit.

{$APPTYPE CONSOLE} 
uses 
    SysUtils; 
const 
    MYVALUE1: Double = 4.5; 
    MYVALUE2: Double = 5; 
    MyCalc: Double = 0.9; 
var 
    MyRoundValue1: Integer; 
    MyRoundValue2: Integer; 
begin 
    Set8087CW($1232); // <-- round intermediates to 64 bit 
    MyRoundValue1 := Round(MYVALUE1); 
    MyRoundValue2 := Round(MYVALUE2 * MyCalc); 
    WriteLn(MyRoundValue1); 
    WriteLn(MyRoundValue2); 
end. 
+0

Tôi sẽ không dám thay đổi từ kiểm soát fpu. –

+1

@LURD Tôi dám. Có rất nhiều kịch bản mà bạn phải làm. Một ví dụ tốt là khi giao dịch với các thư viện bên ngoài. Đôi khi họ không thích nó nếu các ngoại lệ được tiết lộ. Tôi đang nhìn vào bạn Excel 2013. Trong công việc của tôi, nhận được phiên bản 32 bit để hoạt động gần với phiên bản 64 bit là quan trọng. Do đó '$ 1232' là cách phiên bản 32 bit của tôi cuộn. –

+2

@LURD Tất nhiên, như tất cả các bạn phải mệt mỏi của tôi nói rằng, nó không giúp đỡ rằng các chức năng Delphi RTL Set8087CW không phải là threadsafe. Như tôi đã nói rất nhiều lần, tôi đã nói với Emba cách sắp xếp điều này nhưng họ sẽ không làm điều đó. Có lẽ vì họ quá sợ hãi để thay đổi. –

3

System.Round nội chấp nhận một giá trị Extended . Trong các phép tính 32 bit được thực hiện dưới dạng Mở rộng bên trong FPU. Trong 642 bit Mở rộng tương tự như Double. Các đại diện nội bộ có thể chỉ khác nhau mà nhiều để làm cho sự khác biệt.

+0

'Mở rộng' không * tương tự như Double * trong 64bit, nó * IS * một' Double'. 'Mở rộng' trong 32 bit là một kiểu dữ liệu FPU 80 bit gốc, nhưng trong 64bit nó chỉ là một bí danh cho' Double'. Đó là 16 bit độ chính xác bị mất trong các hệ thống 64bit. Đây là [tài liệu] (http://docwiki.embarcadero.com/Libraries/XE8/en/System.Extended): "Trên hệ thống Win32, kích thước của System.Extended là 10 byte. Trên hệ thống Win64, tuy nhiên, * * Loại System.Extended ** là một bí danh cho System.Double, chỉ là 8 byte. Sự khác biệt này có thể ảnh hưởng xấu đến độ chính xác của số trong các phép toán dấu chấm động. " –

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