2010-11-19 34 views
52

Câu hỏi này nảy sinh ra một cái gì đó kỳ lạ mà tôi nhận thấy sau khi điều tra this question thêm ...Trong MATLAB, các biến REALLY có chính xác kép theo mặc định không?

tôi luôn luôn hiểu biến MATLAB là double-precision theo mặc định. Vì vậy, nếu tôi được làm một cái gì đó giống như khai báo một biến với 20 chữ số sau dấu thập phân:

>> num = 2.71828182845904553488; 
>> class(num) %# Display the variable type 
ans = 
double 

Tôi mong chờ 4 chữ số cuối cùng được bỏ qua, kể từ khi floating-point relative accuracy là vào thứ tự của 10 -16:

>> eps(num) 
ans = 
    4.440892098500626e-016 

Nếu tôi cố gắng để hiển thị số với hơn 16 chữ số sau dấu thập phân (sử dụng một trong hai FPRINTF hoặc SPRINTF), tôi nhận được những gì tôi mong đợi để xem:

>> fprintf('%0.20f\n',num) 
2.71828182845904550000 
>> sprintf('%0.20f',num) 
ans = 
2.71828182845904550000 

Nói cách khác, chữ số 17 thông qua 20 đều là 0.

Nhưng mọi thứ trở nên kỳ lạ khi tôi vượt qua num đến variable precision arithmetic function trong Symbolic Toolbox, nói với nó để đại diện cho số sử dụng 21 chữ số chính xác:

>> vpa(num,21) 
ans = 
2.71828182845904553488 

GÌ ?! Bốn chữ số cuối cùng đã xuất hiện trở lại! Không nên họ đã bị mất khi số lượng ban đầu tôi nhập vào được lưu trữ như là một biến chính xác hai lần num? Vì num là biến số chính xác kép khi được chuyển đến vpa, cách vpa biết chúng là gì? Tôi nghĩ tốt nhất là những gì đang xảy ra là MATLAB nội bộ đại diện cho num với độ chính xác cao hơn gấp đôi kể từ khi tôi khởi tạo nó thành một số có nhiều chữ số hơn dấu thập phân so với biến số chính xác kép có thể xử lý. Đây có thực sự là những gì đang xảy ra, hoặc là cái gì khác đang xảy ra?



BONUS: Và đây là một nguồn bổ sung của sự nhầm lẫn nếu bạn chưa có một nửa đầu từ trên ...

>> num = 2.71828182845904553488; %# Declare with 20 digits past the decimal 
>> num = 2.718281828459045531; %# Re-declare with 18 digits past the decimal 
>> vpa(num,21) 
ans = 
2.71828182845904553488 %# It's the original 20-digit number!!! 

Trả lời

51

Họ đôi. Vpa() chỉ đơn giản là chọn hiển thị các chữ số không có nghĩa là vượt quá độ chính xác tương đối của dấu phẩy động, trong đó printf() và disp() cắt ngắn hoặc loại bỏ chúng ra.

Bạn chỉ nhận được bốn chữ số ban đầu của mình vì chữ bạn đã chọn để khởi tạo num với chỉ là giá trị thập phân chính xác của giá trị nhị phân kép, vì nó được sao chép và dán từ đầu ra của phần mở rộng của giá trị gấp đôi thực tế từ câu hỏi khác. Nó sẽ không hoạt động đối với các giá trị lân cận khác, như bạn hiển thị trong phụ lục "THƯỞNG" của bạn.

Chính xác hơn, tất cả các chữ số trong Matlab tạo ra các giá trị kiểu gấp đôi. Chúng được chuyển đổi thành giá trị nhị phân kép gần nhất với giá trị thập phân mà chúng đại diện. Trong thực tế, các chữ số trong một chữ vượt quá giới hạn độ chính xác của loại gấp đôi được giảm âm thầm.Khi bạn sao chép và dán đầu ra của vpa để tạo một biến mới, như áp phích của câu hỏi khác đã làm với câu lệnh "e = ...", bạn đang khởi tạo một giá trị từ một chữ, thay vì xử lý trực tiếp với kết quả của một biểu thức trước đó.

Sự khác biệt ở đây chỉ ở định dạng đầu ra. Tôi nghĩ rằng những gì đang xảy ra là vpa() đang lấy đôi nhị phân chính xác kép đó và coi nó là một giá trị chính xác. Đối với một giá trị số mũ trị số nhị phân đã cho, bạn có thể tính toán số thập phân tương đương với nhiều vị trí thập phân tùy ý. Nếu bạn có độ chính xác giới hạn ("chiều rộng") trong giá trị nhị phân, giống như bạn thực hiện với bất kỳ loại dữ liệu có kích thước cố định nào thì chỉ có rất nhiều chữ số thập phân là đáng kể. Sprintf() và màn hình mặc định của Matlab xử lý điều này bằng cách cắt bớt đầu ra hoặc hiển thị các chữ số không có nghĩa là 0. Vpa() bỏ qua các giới hạn về độ chính xác và tiếp tục tính toán số thập phân như bạn yêu cầu.

Những chữ số bổ sung đó không có thật, theo nghĩa là nếu chúng được thay thế bằng các giá trị khác để tạo ra giá trị thập phân gần đó, chúng sẽ được "làm tròn" thành cùng giá trị nhị phân kép.

Đây là cách hiển thị. Các giá trị này của x đều giống nhau khi được lưu trữ trong đôi, và tất cả sẽ được biểu diễn giống nhau bởi vpa().

x = [ 
    2.7182818284590455348848081484902650117874145507812500 
    2.7182818284590455348848081484902650117874145507819999 
    2.7182818284590455348848 
    2.71828182845904553488485555555555555555555555555555 
    exp(1) 
    ] 
unique(x) 

Đây là một cách khác để chứng minh điều đó. Đây là hai đôi rất gần nhau.

x0 = exp(1) 
x1 = x0 + eps(x0) 

Vpa (x0) và vpa (x1) sẽ tạo ra kết quả đầu ra khác quá nhiều chữ số thứ 16. Tuy nhiên, bạn không thể tạo một giá trị gấp đôi x sao cho vpa (x) tạo ra một biểu diễn thập phân nằm giữa vpa (x0) và vpa (x1).

(UPDATE:.. Amro chỉ ra rằng bạn có thể sử dụng fprintf('%bx\n', x) để hiển thị một đại diện chính xác của giá trị nhị phân tiềm ẩn trong định dạng hex Bạn có thể sử dụng để xác nhận bản đồ literals với cùng kép)

tôi nghi ngờ vpa() hoạt động theo cách này bởi vì nó xử lý các đầu vào của nó như là các giá trị chính xác, và hỗ trợ đa hình các loại Matlab khác từ Hộp công cụ biểu tượng có độ chính xác cao hơn gấp đôi. Các giá trị đó sẽ cần được khởi tạo bằng các phương thức khác với chữ số, đó là lý do tại sao sym() lấy một chuỗi làm đầu vào và "vpa (exp (1))" khác với "vpa (sym ('exp (1)')) ".

Có ý nghĩa? Xin lỗi vì sự dài dòng.

(Lưu ý tôi không có Hộp công cụ biểu tượng để tôi không thể tự kiểm tra vpa().)

+1

+1 để trả lời câu hỏi thú vị! – Jonas

+1

Aha! Vì vậy, tôi đã tình cờ sử dụng việc mở rộng thập phân chính xác của một giá trị nhị phân làm số kiểm tra của tôi. Điều này thật ý nghĩa ngay lúc này! Không chắc làm sao tôi có thể bỏ lỡ điều đó, mặc dù ... có lẽ đó là do thiếu ngủ vì con gái tôi đang mọc răng và giữ tôi suốt đêm. ;) – gnovice

+1

@Andrew: bạn có đang làm việc tại MathWorks không? – Mikhail

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