2017-05-23 20 views
10

Hãy xem xét các chương trình sau đây:răng cưa chặt chẽ, -ffast-toán và SSE

#include <iostream> 
#include <cmath> 
#include <cstring> 
#include <xmmintrin.h> 

using namespace std; 

int main() 
{ 
    // 4 float32s. 
    __m128 nans; 
    // Set them all to 0xffffffff which should be NaN. 
    memset(&nans, 0xff, 4*4); 

    // cmpord should return a mask of 0xffffffff for any non-NaNs, and 0x00000000 for NaNs. 
    __m128 mask = _mm_cmpord_ps(nans, nans); 
    // AND the mask with nans to zero any of the nans. The result should be 0x00000000 for every component. 
    __m128 z = _mm_and_ps(mask, nans); 

    cout << z[0] << " " << z[1] << " " << z[2] << " " << z[3] << endl; 

    return 0; 
} 

Nếu tôi biên dịch với Apple Clang 7.0.2 có và không có -ffast-math, tôi nhận được kết quả mong muốn 0 0 0 0:

$ clang --version 
Apple LLVM version 7.0.2 (clang-700.1.81) 
Target: x86_64-apple-darwin14.5.0 
Thread model: posix 

$ clang test.cpp -o test 
$ ./test 
0 0 0 0 

$ clang test.cpp -ffast-math -o test 
$ ./test 
0 0 0 0 

Tuy nhiên sau khi cập nhật lên 8.1.0 (xin lỗi tôi không biết phiên bản Clang nào tương ứng với - Apple không còn xuất bản thông tin đó nữa), -ffast-math dường như vi phạm điều này:

$ clang --version 
Apple LLVM version 8.1.0 (clang-802.0.42) 
Target: x86_64-apple-darwin16.6.0 
Thread model: posix 
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin 

$ clang test.cpp -o test 
$ ./test 
0 0 0 0 

$ clang test.cpp -ffast-math -o test 
$ ./test 
nan nan nan nan 

Tôi nghi ngờ điều này là do quy tắc bí danh nghiêm ngặt hoặc điều gì đó tương tự. Bất cứ ai có thể giải thích hành vi này?

Chỉnh sửa: Tôi quên đề cập rằng nếu bạn làm nans = { std::nanf(nullptr), ... thì nó hoạt động tốt.

Cũng tìm kiếm trên godbolt có vẻ như hành vi đã thay đổi giữa Clang 3.8.1 và Clang 3.9 - sau này loại bỏ lệnh cmpordps. GCC 7.1 dường như để nó ở.

Trả lời

12

Đây không phải là vấn đề bí mật nghiêm ngặt. Nếu bạn đọc the documentation of -ffast-math, bạn sẽ thấy sự cố của mình:

Bật chế độ toán nhanh. Điều này định nghĩa macro tiền xử lý __FAST_MATH__ và cho phép trình biên dịch đưa ra các giả định tích cực, có khả năng mất khả năng về toán học dấu phẩy động. Chúng bao gồm:

  • [...]
  • toán hạng để dấu chấm động hoạt động không bằng nhau để NaNInf, và
  • [...]

-ffast-math phép trình biên dịch giả định rằng một số dấu phẩy động không bao giờ là NaN (vì nó đặt tùy chọn -ffinite-math-only). Kể từ khi kêu vang cố gắng để phù hợp với tùy chọn gcc, chúng ta có thể đọc một chút từ GCC's option documentation để hiểu rõ hơn những gì -ffinite-math-only làm:

Cho phép tối ưu hóa cho dấu chấm động số học mà cho rằng lập luận và kết quả là không Nans hoặc + -Infs.

Tùy chọn này sẽ không bao giờ được bật bởi bất kỳ tùy chọn -O nào vì nó có thể dẫn đến đầu ra không chính xác cho các chương trình phụ thuộc vào việc triển khai chính xác các quy tắc/thông số IEEE hoặc ISO.

Vì vậy, nếu mã của bạn cần làm việc với NaN, bạn không thể sử dụng -ffast-math hoặc -ffinite-math-only. Nếu không, bạn sẽ gặp rủi ro của trình tối ưu hóa phá hủy mã của bạn, như bạn thấy ở đây.

+0

Huh Tôi không biết rằng ... Cảm ơn! – Timmmm

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