2015-07-26 17 views
11

Mã sau giả định rằng chúng tôi đang sử dụng hệ thống tương thích x86 và long double bản đồ với định dạng 80 bit của x87 FPU.Tại sao giá trị này được in mặc dù là NaN?

#include <cmath> 
#include <array> 
#include <cstring> 
#include <iomanip> 
#include <iostream> 

int main() 
{ 
    std::array<uint8_t,10> data1{0x52,0x23,0x6f,0x24,0x8f,0xac,0xd1,0x43,0x30,0x02}; 
    std::array<uint8_t,10> data2{0x52,0x23,0x6f,0x24,0x8f,0xac,0xd1,0xc3,0x30,0x02}; 
    std::array<uint8_t,10> data3{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x30,0x02}; 
    long double value1, value2, value3; 
    static_assert(sizeof value1 >= 10,"Expected float80"); 
    std::memcpy(&value1, data1.data(),sizeof value1); 
    std::memcpy(&value2, data2.data(),sizeof value2); 
    std::memcpy(&value3, data3.data(),sizeof value3); 
    std::cout << "isnan(value1): " << std::boolalpha << std::isnan(value1) << "\n"; 
    std::cout << "isnan(value2): " << std::boolalpha << std::isnan(value2) << "\n"; 
    std::cout << "isnan(value3): " << std::boolalpha << std::isnan(value3) << "\n"; 
    std::cout << "value1: " << std::setprecision(20) << value1 << "\n"; 
    std::cout << "value2: " << std::setprecision(20) << value2 << "\n"; 
    std::cout << "value3: " << std::setprecision(20) << value3 << "\n"; 
} 

Output:

isNaN (value1): true

isNaN (value2): false

isNaN (value3): false

value1: 3.3614005946481929011e- 4764

giá trị2: 9.705 6260598879139386e-4764

value3: 6.3442254652397210376e-4764

Đây value1 được phân loại là "không được hỗ trợ" bởi 387 và cao hơn, bởi vì nó có khác không và không phải tất cả-những số mũ - đó là thực chất là một "unnormal" . Và isnan hoạt động như mong đợi với nó: giá trị thực sự là không có gì của một số (mặc dù không chính xác là một NaN). Giá trị thứ hai, value2, có bộ bit số nguyên đó và cũng hoạt động như mong đợi: không phải là NaN. Giá trị thứ ba là giá trị của bit số nguyên còn thiếu.

Nhưng bằng cách nào đó cả hai số value1value2 xuất hiện được in và các giá trị khác nhau chính xác bởi bit số nguyên bị thiếu! Tại sao vậy? Tất cả các phương pháp khác tôi đã thử, như printfto_string chỉ cung cấp 0.00000.

Ngay cả người lạ, nếu tôi thực hiện bất kỳ số học nào với value1, trong các bản in tiếp theo, tôi nhận được nan. Tính đến điều này, làm cách nào để operator<<(long double) thậm chí quản lý thực sự in bất kỳ thứ gì trừ nan? Liệu nó có thiết lập một cách rõ ràng bit nguyên, hay có thể nó phân tích số thay vì thực hiện bất kỳ số học FPU nào trên nó? (giả sử g ++ 4.8 trên Linux 32 bit).

+0

Điều này có thể tương tự như http://stackoverflow.com/questions/4518951/engineered-bool-compares-equal-to-both-true-and-false-why. Việc thực hiện có quyền giả định rằng những gì bạn cung cấp cho nó chứa một biểu diễn 'long double' hợp lệ (hoặc' bool' trong câu hỏi được liên kết). Nếu bạn vượt qua nó một đại diện không hợp lệ, tất cả các cược được tắt. –

+0

UB là U. Tại sao bạn nên mong đợi bất kỳ hành vi cụ thể nào? –

+6

“trên thực tế nó là" không bình thường ". Và isnan hoạt động như mong đợi với nó: giá trị thực sự không là gì cả. ”Đây không phải là ý nghĩa của“ NaN ”. NaN là một giá trị dấu phẩy động sẽ truyền qua các tính toán theo một cách xác định. Một giá trị không bình thường là một dấu tích lịch sử và nó không được hỗ trợ để truyền một cho một lệnh dấu phẩy động kể từ 387. Vì mục đích thực tế, một giá trị không bình thường nên được coi là một “đại diện bẫy”. . –

Trả lời

1

Tất cả các phương pháp khác tôi đã thử, như printf và to_string chỉ cung cấp 0,00000.

operator<<(long double) thực sự thực hiện được bằng cách sử dụng lớp num_put<> từ locale thư viện để thực hiện các định dạng số, do đó sử dụng một trong những chức năng printf -family (xem phần 27.7.3.6 và 22.4.2.2 của chuẩn C++) .

Tùy thuộc vào các cài đặt, chuyển đổi printf specifier sử dụng cho long double bởi locale có thể là bất kỳ: %Lf, %Le, %LE, %La, %LA, %Lg hoặc %LG.

Trong trường hợp (và tôi) của bạn nó có vẻ là %Lg:

printf("value1: %.20Lf\n", value1); 
printf("value1: %.20Le\n", value1); 
printf("value1: %.20La\n", value1); 
printf("value1: %.20Lg\n", value1); 
std::cout << "value1: " << std::setprecision(20) << value1 << "\n"; 

value1: 0,00000000000000000000

value1: 3.36140059464819290106e-4764

value1: 0x4.3d1ac8f246f235200000p-15.826

value1: 3.3614005946481929011e-4764

value1: 3.3614005946481929011e-4764


Đi vào tài khoản, nhà điều hành hoạt động như thế nào < < (dài gấp đôi) thậm chí quản lý để actuall y in gì ngoài nan? Liệu nó có thiết lập một cách rõ ràng các số nguyên bit, hoặc có thể nó phân tích số thay vì làm bất kỳ số học FPU trên nó?

Nó in giá trị không chuẩn hóa.

Chuyển đổi từ biểu thức điểm số nhị phân sang dấu phẩy động thập phân được sử dụng bởi printf() có thể được thực hiện mà không cần bất kỳ điểm FPU nào. Bạn có thể tìm thấy triển khai glibc trong tệp nguồn stdio-common/printf_fp.c.

+0

Câu trả lời hay! Cảm ơn. – Ruslan

0

Tôi đã cố gắng này:

long double value = std::numeric_limits<long double>::quiet_NaN(); 
std::cout << "isnan(value): " << std::boolalpha << std::isnan(value) << "\n"; 
std::cout << "value: " << std::setprecision(20) << value << "\n"; 

Vì vậy, tôi giả định là như đã nêu ở đây: http://en.cppreference.com/w/cpp/numeric/math/isnan giá trị đã được đúc sẽ tăng gấp đôi và không lâu đôi khi được đánh giá bởi std::isnan và chặt chẽ:

std::numeric_limits<long double>::quiet_NaN() != std::numeric_limits<double>::quiet_NaN() 
+0

NaN là theo định nghĩa bất bình đẳng với chính nó cũng như bất cứ điều gì khác, bất kể loại. – Ruslan

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