Câu hỏi Tại sao GCC không tối ưu hóa a * a * a * a * a * a (a * a * a) * (a * a * a)?


Tôi đang làm một số tối ưu hóa số trên một ứng dụng khoa học. Một điều tôi nhận thấy là GCC sẽ tối ưu hóa cuộc gọi pow(a,2) bằng cách biên dịch nó thành a*a, nhưng cuộc gọi pow(a,6) không được tối ưu hóa và thực sự sẽ gọi hàm thư viện pow, làm chậm đáng kể hiệu suất. (Ngược lại, Trình biên dịch Intel C ++, thực thi icc, sẽ loại bỏ cuộc gọi thư viện cho pow(a,6).)

Điều tôi tò mò là khi tôi thay thế pow(a,6) với a*a*a*a*a*a sử dụng GCC 4.5.1 và các tùy chọn "-O3 -lm -funroll-loops -msse4", nó sử dụng 5 mulsd hướng dẫn:

movapd  %xmm14, %xmm13
mulsd   %xmm14, %xmm13
mulsd   %xmm14, %xmm13
mulsd   %xmm14, %xmm13
mulsd   %xmm14, %xmm13
mulsd   %xmm14, %xmm13

trong khi nếu tôi viết (a*a*a)*(a*a*a), nó sẽ sản xuất

movapd  %xmm14, %xmm13
mulsd   %xmm14, %xmm13
mulsd   %xmm14, %xmm13
mulsd   %xmm13, %xmm13

làm giảm số lượng hướng dẫn nhân với 3. icc có hành vi tương tự.

Tại sao trình biên dịch không nhận ra mẹo tối ưu hóa này?


1965
2018-06-21 18:49


gốc


"Công nhận pow (a, 6)" nghĩa là gì? - Varun Madiath
Um ... bạn biết rằngmộtmộtmộtmộta và (amộta) * (aa * a) không giống với số dấu chấm động, phải không? Bạn sẽ phải sử dụng -funsafe-math hoặc -ffast-math hoặc một cái gì đó cho điều đó. - Damon
Tôi đề nghị bạn nên đọc "Mỗi nhà khoa học máy tính nên biết gì về số học dấu chấm động" của David Goldberg: download.oracle.com/docs/cd/E19957-01/806-3568/… sau đó bạn sẽ có một sự hiểu biết đầy đủ hơn về hố tar mà bạn vừa mới bước vào! - Phil Armstrong
Một câu hỏi hoàn toàn hợp lý. 20 năm trước, tôi đã hỏi cùng một câu hỏi chung, và bằng cách nghiền nát nút cổ chai đơn, giảm thời gian thực hiện của một mô phỏng Monte Carlo từ 21 giờ đến 7 giờ. Mã trong vòng lặp bên trong đã được thực hiện 13 nghìn tỷ lần trong quá trình, nhưng nó đã mô phỏng thành một cửa sổ qua đêm. (xem câu trả lời dưới đây)
Có thể ném (a*a)*(a*a)*(a*a) vào hỗn hợp, quá. Cùng một số phép nhân, nhưng có lẽ chính xác hơn. - Rok Kralj


Các câu trả lời:


Bởi vì Toán điểm nổi không phải là kết hợp. Cách bạn nhóm các toán hạng trong phép nhân dấu chấm động có ảnh hưởng đến tính chính xác của câu trả lời.

Kết quả là, hầu hết các trình biên dịch rất bảo thủ về việc sắp xếp lại các phép tính dấu chấm động trừ khi chúng có thể chắc chắn rằng câu trả lời sẽ giữ nguyên hoặc trừ khi bạn nói với chúng rằng bạn không quan tâm về độ chính xác về số. Ví dụ: các -fassociative-math Tùy chọn của gcc cho phép gcc liên kết lại các phép toán dấu chấm động, hoặc thậm chí là -ffast-math tùy chọn cho phép sự cân bằng tích cực hơn về độ chính xác so với tốc độ.


2567
2018-06-22 15:32



Vâng. Với -ffast-math nó đang làm tối ưu hóa như vậy. Ý tưởng tốt! Nhưng vì mã của chúng tôi có độ chính xác cao hơn tốc độ, nên tốt hơn là không nên vượt qua nó. - xis
IIRC C99 cho phép trình biên dịch thực hiện tối ưu hóa "không an toàn" như vậy, nhưng GCC (trên bất kỳ thứ gì khác ngoài x87) tạo ra một nỗ lực hợp lý theo IEEE 754 - nó không phải là "giới hạn lỗi"; chỉ có một câu trả lời đúng. - tc.
Các chi tiết thực hiện của pow không ở đây cũng không có; câu trả lời này thậm chí không tham khảo pow. - Stephen Canon
@nedR: ICC mặc định cho phép liên kết lại. Nếu bạn muốn có hành vi phù hợp tiêu chuẩn, bạn cần phải đặt -fp-model precise với ICC. clang và gcc mặc định tuân thủ nghiêm ngặt w.r.t. phân phối lại. - Stephen Canon
@ xis, nó không thực sự là -fassociative-math sẽ không chính xác; nó chỉ là a*a*a*a*a*a và (a*a*a)*(a*a*a) khác nhau. Nó không phải về độ chính xác; đó là về sự phù hợp tiêu chuẩn và kết quả có thể lặp lại nghiêm ngặt, ví dụ: cùng một kết quả trên bất kỳ trình biên dịch nào. Số điểm nổi đã không chính xác. Nó hiếm khi không thích hợp để biên dịch -fassociative-math. - Paul Draper


Lambdageek chính xác chỉ ra rằng bởi vì tính kết hợp không giữ cho các số dấu phẩy động, "tối ưu hóa" của a*a*a*a*a*a đến (a*a*a)*(a*a*a) có thể thay đổi giá trị. Đây là lý do tại sao nó không được phép bởi C99 (trừ khi được cho phép bởi người dùng, thông qua cờ trình biên dịch hoặc pragma). Nói chung, giả định là lập trình viên đã viết những gì cô ấy đã làm cho một lý do, và trình biên dịch nên tôn trọng điều đó. Nếu bạn muốn (a*a*a)*(a*a*a), viết đi.

Đó có thể là một nỗi đau để viết, mặc dù; tại sao trình biên dịch không thể làm [điều bạn cho là] điều đúng khi bạn sử dụng pow(a,6)? Bởi vì nó sẽ là sai rồi điều cần làm. Trên một nền tảng với một thư viện toán học tốt, pow(a,6) chính xác hơn đáng kể so với a*a*a*a*a*a hoặc là (a*a*a)*(a*a*a). Chỉ để cung cấp một số dữ liệu, tôi đã chạy một thử nghiệm nhỏ trên máy Mac Pro của tôi, đo lỗi tồi tệ nhất trong việc đánh giá ^ 6 cho tất cả các số nổi chính xác đơn giữa [1,2):

worst relative error using    powf(a, 6.f): 5.96e-08
worst relative error using (a*a*a)*(a*a*a): 2.94e-07
worst relative error using     a*a*a*a*a*a: 2.58e-07

Sử dụng pow thay vì cây nhân làm giảm lỗi bị ràng buộc bởi hệ số 4. Trình biên dịch không nên (và thường không) làm cho "tối ưu hóa" làm tăng lỗi trừ khi được cấp phép để làm như vậy bởi người dùng (ví dụ: thông qua -ffast-math).

Lưu ý rằng GCC cung cấp __builtin_powi(x,n) thay thế cho pow( ), sẽ tạo ra một cây nhân nội tuyến. Sử dụng điều đó nếu bạn muốn giao dịch chính xác cho hiệu suất, nhưng không muốn kích hoạt tính toán nhanh.


614
2018-06-22 22:39



Cũng lưu ý rằng Visual C ++ cung cấp một phiên bản 'nâng cao' của pow (). Bằng cách gọi _set_SSE2_enable(<flag>) với flag=1, nó sẽ sử dụng SSE2 nếu có thể. Điều này làm giảm độ chính xác một chút, nhưng cải thiện tốc độ (trong một số trường hợp). MSDN: _set_SSE2_enable () và pow () - TkTech
@TkTech: Bất kỳ độ chính xác giảm nào là do việc triển khai của Microsoft, không phải kích thước của thanh ghi được sử dụng. Có thể phân phối được làm tròn chính xác  pow chỉ sử dụng thanh ghi 32 bit, nếu người viết thư viện có động lực như vậy. Có dựa trên SSE pow triển khai hơn chính xác hơn so với hầu hết các triển khai dựa trên x87 và cũng có các triển khai thực hiện một số độ chính xác cho tốc độ. - Stephen Canon
@TkTech: Tất nhiên, tôi chỉ muốn làm rõ rằng việc giảm độ chính xác là do sự lựa chọn của các nhà văn thư viện, không phải là nội tại đối với việc sử dụng SSE. - Stephen Canon
Tôi quan tâm để biết những gì bạn sử dụng như là "tiêu chuẩn vàng" ở đây để tính toán các lỗi tương đối - tôi thường sẽ mong đợi nó sẽ là a*a*a*a*a*a, nhưng đó rõ ràng không phải là trường hợp! :) - j_random_hacker
@j_random_hacker: vì tôi đã so sánh kết quả chính xác đơn, độ chính xác kép đủ cho tiêu chuẩn vàng - lỗi từmộtmộtmộtmộttính gấp đôi là * bao la nhỏ hơn lỗi của bất kỳ phép tính đơn chính xác nào. - Stephen Canon


Một trường hợp tương tự khác: hầu hết các trình biên dịch sẽ không tối ưu hóa a + b + c + d đến (a + b) + (c + d) (đây là một tối ưu hóa vì biểu thức thứ hai có thể được pipelined tốt hơn) và đánh giá nó như được đưa ra (tức là (((a + b) + c) + d)). Điều này cũng là do các trường hợp góc:

float a = 1e35, b = 1e-5, c = -1e35, d = 1e-5;
printf("%e %e\n", a + b + c + d, (a + b) + (c + d));

Kết quả đầu ra này 1.000000e-05 0.000000e+00


152
2018-06-23 11:44



Điều này là không chính xác như nhau. Thay đổi thứ tự của phép nhân / phân chia (không bao gồm phép chia cho 0) an toàn hơn so với thứ tự thay đổi của tổng / phép trừ. Theo quan điểm khiêm tốn của tôi, trình biên dịch nên cố gắng kết hợp mults./divs. bởi vì làm điều đó làm giảm tổng số hoạt động và bên cạnh hiệu suất đạt được đó cũng là một độ chính xác đạt được. - GameDeveloper
@DarioOO: Nó không an toàn hơn. Nhân và chia đều giống như cộng và trừ của số mũ, và việc thay đổi thứ tự có thể dễ dàng gây ra thời gian vượt quá phạm vi có thể của số mũ. (Không chính xác như nhau, bởi vì số mũ không bị mất độ chính xác ... nhưng biểu diễn vẫn còn khá hạn chế, và sắp xếp lại có thể dẫn đến các giá trị không thể đại diện) - Ben Voigt
Tôi nghĩ rằng bạn đang thiếu một số nền tính toán. Multplying và phân chia 2 số giới thiệu cùng một số lượng lỗi. Trong khi trừ / thêm 2 số có thể đưa ra một lỗi lớn hơn, đặc biệt khi 2 số là thứ tự các độ lớn khác nhau, do đó nó được sắp xếp lại một cách an toàn hơn so với phụ / cộng bởi vì nó giới thiệu một sự thay đổi nhỏ trong lỗi cuối cùng. - GameDeveloper
@DarioOO: rủi ro khác với mul / div: Sắp xếp lại hoặc thay đổi không đáng kể trong kết quả cuối cùng, hoặc số mũ tràn ở một số điểm (nơi nó không có trước đó) và kết quả là khác nhau (có khả năng + inf hoặc 0). - Peter Cordes


Fortran (được thiết kế cho máy tính khoa học) có một nhà điều hành điện tích hợp, và theo như tôi biết, các trình biên dịch Fortran thường sẽ tối ưu hóa việc nâng cấp lên các số nguyên theo cách tương tự với những gì bạn mô tả. C / C + + tiếc là không có một nhà điều hành điện, chỉ có chức năng thư viện pow(). Điều này không ngăn các trình biên dịch thông minh xử lý pow đặc biệt và tính toán nó một cách nhanh hơn cho các trường hợp đặc biệt, nhưng có vẻ như họ làm điều đó ít phổ biến hơn ...

Một vài năm trước, tôi đã cố gắng để làm cho nó thuận tiện hơn để tính toán năng lượng số nguyên một cách tối ưu, và đưa ra sau đây. Đó là C ++, không phải C mặc dù, và vẫn còn phụ thuộc vào trình biên dịch được phần nào thông minh về cách tối ưu hóa / nội tuyến những thứ. Dù sao, hy vọng bạn có thể thấy nó hữu ích trong thực tế:

template<unsigned N> struct power_impl;

template<unsigned N> struct power_impl {
    template<typename T>
    static T calc(const T &x) {
        if (N%2 == 0)
            return power_impl<N/2>::calc(x*x);
        else if (N%3 == 0)
            return power_impl<N/3>::calc(x*x*x);
        return power_impl<N-1>::calc(x)*x;
    }
};

template<> struct power_impl<0> {
    template<typename T>
    static T calc(const T &) { return 1; }
};

template<unsigned N, typename T>
inline T power(const T &x) {
    return power_impl<N>::calc(x);
}

Làm rõ cho tò mò: điều này không tìm ra cách tối ưu để tính toán quyền hạn, nhưng vì tìm ra giải pháp tối ưu là vấn đề NP-complete và điều này chỉ đáng làm cho các quyền hạn nhỏ (trái với việc sử dụng pow), không có lý do gì để làm phiền các chi tiết.

Sau đó, chỉ cần sử dụng nó như power<6>(a).

Điều này làm cho nó dễ dàng để loại quyền hạn (không cần phải đánh vần 6 as với parens) và cho phép bạn có loại tối ưu hóa này mà không cần -ffast-math trong trường hợp bạn có một cái gì đó chính xác phụ thuộc như tổng kết bù (một ví dụ nơi thứ tự các hoạt động là cần thiết).

Bạn cũng có thể quên rằng đây là C ++ và chỉ sử dụng nó trong chương trình C (nếu nó biên dịch với trình biên dịch C ++).

Hy vọng điều này có thể hữu ích.

CHỈNH SỬA:

Đây là những gì tôi nhận được từ trình biên dịch của tôi:

Dành cho a*a*a*a*a*a,

    movapd  %xmm1, %xmm0
    mulsd   %xmm1, %xmm0
    mulsd   %xmm1, %xmm0
    mulsd   %xmm1, %xmm0
    mulsd   %xmm1, %xmm0
    mulsd   %xmm1, %xmm0

Dành cho (a*a*a)*(a*a*a),

    movapd  %xmm1, %xmm0
    mulsd   %xmm1, %xmm0
    mulsd   %xmm1, %xmm0
    mulsd   %xmm0, %xmm0

Dành cho power<6>(a),

    mulsd   %xmm0, %xmm0
    movapd  %xmm0, %xmm1
    mulsd   %xmm0, %xmm1
    mulsd   %xmm0, %xmm1

74
2018-06-23 10:07



Việc tìm kiếm cây quyền lực tối ưu có thể khó, nhưng vì nó chỉ thú vị với các quyền hạn nhỏ, câu trả lời rõ ràng là tính toán trước một lần (Knuth cung cấp một bảng lên đến 100) và sử dụng bảng mã cứng đó (đó là gcc thực hiện nội bộ cho powi) . - Marc Glisse
Trên các bộ xử lý hiện đại, tốc độ bị giới hạn bởi độ trễ. Ví dụ, kết quả của một phép nhân có thể có sẵn sau năm chu kỳ. Trong tình huống đó, việc tìm ra cách nhanh nhất để tạo ra sức mạnh có thể phức tạp hơn. - gnasher729
Bạn cũng có thể thử tìm cây điện cung cấp giới hạn trên thấp nhất cho lỗi làm tròn tương đối hoặc lỗi làm tròn tương đối trung bình thấp nhất. - gnasher729
Boost cũng hỗ trợ cho điều này, ví dụ: boost :: math :: pow <6> (n); Tôi nghĩ rằng nó thậm chí còn cố gắng giảm số lượng phép nhân bằng cách trích xuất các yếu tố chung. - gast128
Ý kiến ​​hay ! Tôi đã làm điều đó cho precomputing giai thừa. - Caduchon


Bởi vì một số dấu phẩy động 32 bit - chẳng hạn như 1.024 - không phải là 1.024. Trong máy tính, 1.024 là một khoảng thời gian: từ (1.024-e) đến (1.024 + e), trong đó "e" biểu thị lỗi. Một số người không nhận ra điều này và cũng tin rằng * trong dấu * là viết tắt của phép nhân các số có độ chính xác tùy ý mà không có bất kỳ lỗi nào gắn liền với những con số đó. Lý do tại sao một số người không nhận ra điều này có lẽ là tính toán toán học mà họ thực hiện ở trường tiểu học: chỉ làm việc với những con số lý tưởng mà không kèm theo lỗi và tin rằng chỉ cần bỏ qua "e" trong khi thực hiện phép nhân. Họ không thấy "e" ngầm trong "float a = 1.2", "a * a * a" và các mã C tương tự.

Nên phần lớn các lập trình viên nhận ra (và có thể thực hiện trên) ý tưởng rằng biểu thức C a * a * a * a * a * a không thực sự làm việc với các số lý tưởng, trình biên dịch GCC sau đó sẽ MIỄN PHÍ để tối ưu hóa "a * a * a * a * a * a "thành ngữ" t = (a * a); t * t * t "yêu cầu số phép nhân nhỏ hơn. Nhưng thật không may, trình biên dịch GCC không biết liệu lập trình viên viết mã có nghĩ rằng "a" là một số có hoặc không có lỗi. Và như vậy GCC sẽ chỉ làm những gì mã nguồn trông như thế - bởi vì đó là những gì GCC nhìn thấy bằng "mắt thường" của nó.

... một khi bạn biết loại lập trình viên nào bạn là, bạn có thể sử dụng công tắc "-ffast-math" để nói với GCC rằng "Này, GCC, tôi biết tôi đang làm gì!". Điều này sẽ cho phép GCC chuyển đổi * a * a * a * a * thành một phần văn bản khác - nó trông khác với một * a * a * a * a * a - nhưng vẫn tính toán một số trong khoảng thời gian lỗi của a * a * a * a * a * a. Điều này là OK, vì bạn đã biết bạn đang làm việc với khoảng thời gian, không phải là số lý tưởng.


49
2018-03-29 06:51



Số điểm nổi là chính xác. Họ không nhất thiết phải chính xác những gì bạn mong đợi. Hơn nữa, kỹ thuật với epsilon chính nó là một xấp xỉ để giải quyết mọi thứ trong thực tế, bởi vì sai số dự kiến ​​thực sự là tương đối so với tỉ lệ của mantissa, tức là, bạn thường lên đến khoảng 1 LSB, nhưng có thể tăng lên mọi hoạt động được thực hiện nếu bạn không cẩn thận để tham khảo ý kiến ​​một nhà phân tích số trước khi làm bất cứ điều gì không tầm thường với điểm nổi. Sử dụng một thư viện thích hợp nếu bạn có thể có thể. - Donal Fellows
@DonalFellows: Tiêu chuẩn IEEE yêu cầu tính toán điểm động mang lại kết quả khớp chính xác nhất với kết quả sẽ là gì nếu toán hạng nguồn là giá trị chính xác, nhưng điều đó không có nghĩa là chúng thực sự đại diện giá trị chính xác. Trong nhiều trường hợp hữu ích hơn khi xem 0.1f là (1.677.722 +/- 0.5) / 16,777,216, sẽ được hiển thị với số chữ số thập phân được ngụ ý bởi sự không chắc chắn đó, hơn là số lượng chính xác (1.677,722 +/- 0,5) / 16,777,216 (sẽ được hiển thị thành 24 chữ số thập phân). - supercat
@supercat: IEEE-754 khá rõ ràng về điểm dữ liệu dấu phẩy động làm đại diện cho các giá trị chính xác; các điều khoản 3.2 - 3.4 là các phần liên quan. Dĩ nhiên, bạn có thể chọn giải thích chúng theo cách khác, giống như bạn có thể chọn để diễn giải int x = 3 như ý nghĩa x là 3 +/- 0,5. - Stephen Canon
@supercat: Tôi đồng ý hoàn toàn, nhưng điều đó không có nghĩa là Distance không chính xác bằng giá trị số của nó; nó có nghĩa là giá trị số chỉ là một xấp xỉ với một số lượng vật lý được mô hình hoá. - Stephen Canon
Để phân tích bằng số, bộ não của bạn sẽ cảm ơn bạn nếu bạn giải thích các số dấu phẩy động không phải là khoảng thời gian, mà là các giá trị chính xác (không xảy ra chính xác giá trị bạn muốn). Ví dụ, nếu x ở đâu đó tròn 4,5 với một lỗi nhỏ hơn 0,1, và bạn tính toán (x + 1) - x, "khoảng thời gian" giải thích lá bạn với một khoảng thời gian 0,8-1,2, trong khi "chính xác giá trị" bạn kết quả sẽ là 1 với sai số tối đa là 2 ^ (- 50) với độ chính xác gấp đôi. - gnasher729


GCC thực sự tối ưu hóa a * a * a * a * a * a thành (a * a * a) * (a * a * a) khi a là một số nguyên. Tôi đã thử với lệnh này:

$ echo 'int f(int x) { return x*x*x*x*x*x; }' | gcc -o - -O2 -S -masm=intel -x c -

Có rất nhiều cờ gcc nhưng không có gì lạ mắt. Họ có nghĩa là: Đọc từ stdin; sử dụng mức tối ưu hóa O2; danh sách ngôn ngữ lắp ráp đầu ra thay vì một nhị phân; danh sách nên sử dụng cú pháp ngôn ngữ assembly Intel; đầu vào là trong ngôn ngữ C (thường là ngôn ngữ được suy ra từ phần mở rộng tập tin đầu vào, nhưng không có phần mở rộng tập tin khi đọc từ stdin); và viết cho stdout.

Đây là phần quan trọng của đầu ra. Tôi đã chú thích nó với một số ý kiến ​​cho thấy những gì đang xảy ra trong ngôn ngữ lắp ráp:

    ; x is in edi to begin with.  eax will be used as a temporary register.
    mov    eax, edi     ; temp1 = x
    imul    eax, edi    ; temp2 = x * temp1
    imul    eax, edi    ; temp3 = x * temp2
    imul    eax, eax    ; temp4 = temp3 * temp3

Tôi đang sử dụng hệ thống GCC trên Linux Mint 16 Petra, một dẫn xuất Ubuntu. Đây là phiên bản gcc:

$ gcc --version
gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1

Như các áp phích khác đã lưu ý, tùy chọn này là không thể trong điểm nổi, bởi vì số học dấu chấm động thực sự không phải là kết hợp.


49
2018-06-27 21:03



Điều này là hợp pháp cho phép nhân số nguyên vì tràn bổ sung của hai là hành vi không xác định. Nếu có sẽ là một tràn, nó sẽ xảy ra ở đâu đó, bất kể sắp xếp lại hoạt động. Vì vậy, các biểu thức không có tràn đánh giá giống nhau, các biểu thức tràn là hành vi không xác định, do đó, ok cho trình biên dịch thay đổi điểm mà tại đó tràn xảy ra. gcc thực hiện điều này với unsigned int, quá. - Peter Cordes


Chưa có áp phích nào đề cập đến sự co lại của các biểu thức nổi (tiêu chuẩn ISO C, 6.5p8 và 7.12.2). Nếu FP_CONTRACT pragma được đặt thành ON, trình biên dịch được phép xem một biểu thức như a*a*a*a*a*a như một thao tác đơn lẻ, như thể được đánh giá chính xác với một lần làm tròn đơn. Ví dụ, một trình biên dịch có thể thay thế nó bằng chức năng nguồn bên trong vừa nhanh hơn vừa chính xác hơn. Điều này đặc biệt thú vị vì hành vi được kiểm soát một phần bởi lập trình viên trực tiếp trong mã nguồn, trong khi các tùy chọn trình biên dịch do người dùng cuối cung cấp đôi khi có thể được sử dụng không chính xác.

Trạng thái mặc định của FP_CONTRACT pragma được thực hiện xác định, do đó trình biên dịch được phép thực hiện tối ưu hóa như vậy theo mặc định. Do đó, mã di động cần tuân thủ nghiêm ngặt các quy tắc IEEE 754 nên đặt rõ ràng OFF.

Nếu trình biên dịch không hỗ trợ pragma này, trình biên dịch phải bảo thủ bằng cách tránh bất kỳ tối ưu hóa nào như vậy, trong trường hợp nhà phát triển đã chọn đặt nó thành OFF.

GCC không hỗ trợ pragma này, nhưng với các tùy chọn mặc định, GCC cho rằng ON; do đó đối với các mục tiêu có FMA phần cứng, nếu muốn ngăn chặn chuyển đổi a*b+c để fma (a, b, c), một trong những nhu cầu để cung cấp một tùy chọn như -ffp-contract=off (để đặt rõ ràng pragma thành OFF) hoặc là -std=c99 (để nói với GCC để phù hợp với một số phiên bản tiêu chuẩn C, ở đây C99, do đó làm theo đoạn trên). Trong quá khứ, tùy chọn thứ hai không ngăn cản việc chuyển đổi, có nghĩa là GCC không phù hợp với điểm này: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=37845


27
2018-06-23 12:44



Các câu hỏi phổ biến lâu đời đôi khi hiển thị tuổi của chúng. Câu hỏi này được hỏi và trả lời vào năm 2011, khi GCC có thể được miễn trừ vì không tôn trọng chính xác tiêu chuẩn C99 gần đây. Tất nhiên giờ là năm 2014, nên GCC ... ahem. - Pascal Cuoq
Bạn có nên trả lời các câu hỏi dấu chấm động tương đối gần đây mà không có câu trả lời được chấp nhận thay thế không? ho stackoverflow.com/questions/23703408 ho - Pascal Cuoq
Tôi thấy nó ... xáo trộn rằng gcc không thực hiện các pragma điểm nổi C99. - David Monniaux


Như Lambdageek chỉ ra phép nhân không phải là kết hợp và bạn có thể nhận được độ chính xác thấp hơn, nhưng cũng khi có độ chính xác tốt hơn, bạn có thể tranh luận chống lại tối ưu hóa, bởi vì bạn muốn một ứng dụng xác định. Ví dụ như trong máy khách / máy chủ mô phỏng trò chơi, trong đó mỗi khách hàng phải mô phỏng cùng một thế giới, bạn muốn các phép tính điểm động được xác định.


26
2018-06-21 18:52



Điểm nổi luôn luôn xác định. - Alice
@Alice Nó có vẻ khá rõ ràng Bjorn ở đây là sử dụng 'xác định' trong ý nghĩa của mã cho kết quả tương tự trên các nền tảng khác nhau và phiên bản trình biên dịch khác nhau vv (biến bên ngoài có thể vượt ra ngoài sự kiểm soát của lập trình viên) - trái ngược với thiếu của ngẫu nhiên số thực tế tại thời gian chạy. Nếu bạn đang chỉ ra rằng đây không phải là cách sử dụng đúng từ, tôi sẽ không tranh luận với điều đó. - greggo
@greggo Ngoại trừ ngay cả trong cách giải thích của bạn về những gì ông nói, nó vẫn còn sai; đó là toàn bộ điểm của IEEE 754, để cung cấp các đặc điểm giống nhau cho hầu hết các hoạt động (nếu không phải tất cả) trên các nền tảng. Bây giờ, anh ta không đề cập đến nền tảng hay phiên bản trình biên dịch, mà sẽ là một mối quan tâm hợp lệ nếu bạn muốn mọi hoạt động đơn lẻ trên mọi máy khách / máy khách từ xa giống nhau .... nhưng điều này không rõ ràng từ tuyên bố của anh ta. Một từ tốt hơn có thể là "tương tự đáng tin cậy" hoặc một cái gì đó. - Alice
@Alice bạn đang lãng phí thời gian của mọi người, bao gồm cả của riêng bạn, bằng cách tranh luận ngữ nghĩa. Ý nghĩa của anh ấy rõ ràng. - Lanaru
@ Lanaru Toàn bộ điểm của tiêu chuẩn IS ngữ nghĩa; ý nghĩa của anh ta không rõ ràng. - Alice