ทำไม NaN - NaN == 0.0 กับ Intel C ++ Compiler


300

เป็นที่ทราบกันดีว่า NaNs เผยแพร่เลขคณิต แต่ฉันไม่พบการสาธิตดังนั้นฉันจึงเขียนการทดสอบเล็กน้อย:

#include <limits>
#include <cstdio>

int main(int argc, char* argv[]) {
    float qNaN = std::numeric_limits<float>::quiet_NaN();

    float neg = -qNaN;

    float sub1 = 6.0f - qNaN;
    float sub2 = qNaN - 6.0f;
    float sub3 = qNaN - qNaN;

    float add1 = 6.0f + qNaN;
    float add2 = qNaN + qNaN;

    float div1 = 6.0f / qNaN;
    float div2 = qNaN / 6.0f;
    float div3 = qNaN / qNaN;

    float mul1 = 6.0f * qNaN;
    float mul2 = qNaN * qNaN;

    printf(
        "neg: %f\nsub: %f %f %f\nadd: %f %f\ndiv: %f %f %f\nmul: %f %f\n",
        neg, sub1,sub2,sub3, add1,add2, div1,div2,div3, mul1,mul2
    );

    return 0;
}

ตัวอย่าง ( แสดงสดที่นี่ ) สร้างสิ่งที่ฉันคาดหวัง (เชิงลบค่อนข้างแปลก แต่มันก็สมเหตุสมผล):

neg: -nan
sub: nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan

MSVC 2015 ผลิตสิ่งที่คล้ายกัน อย่างไรก็ตาม Intel C ++ 15 ผลิต:

neg: -nan(ind)
sub: nan nan 0.000000
add: nan nan
div: nan nan nan
mul: nan nan

โดยเฉพาะ, qNaN - qNaN == 0.0.

นี่ ... ไม่ถูกต้องใช่ไหม มาตรฐานที่เกี่ยวข้อง (ISO C, ISO C ++, IEEE 754) พูดถึงอะไรและทำไมจึงมีพฤติกรรมที่แตกต่างกันระหว่างคอมไพเลอร์?


18
Javascript และ Python (จำนวนมาก) ไม่มีลักษณะการทำงานนี้ เป็นNan-NaN NaNPerl และ Scala ก็มีพฤติกรรมเช่นเดียวกัน
พอล

33
คุณอาจเปิดใช้งานการเพิ่มประสิทธิภาพคณิตศาสตร์ที่ไม่ปลอดภัย (เทียบเท่า-ffast-mathกับ gcc) หรือไม่
Matteo Italia

5
@nm: ไม่เป็นความจริง ภาคผนวก F ซึ่งจะเป็นตัวเลือก แต่กฎเกณฑ์เมื่อได้รับการสนับสนุนและความจำเป็นที่จะมีพฤติกรรมลอยจุดที่ระบุไว้ในทุกหลักประกอบด้วยอีอีอี 754 ซีเข้า
R .. GitHub ช่วยหยุด ICE

5
หากคุณต้องการถามเกี่ยวกับมาตรฐาน IEEE 754 ให้พูดถึงที่อื่นในคำถาม
n คำสรรพนาม 'm

68
ฉันแน่ใจว่าคำถามนี้เกี่ยวกับ JavaScript จากชื่อ
MikeTheLiar

คำตอบ:


300

จัดการใน Intel C ++ คอมไพเลอร์ลอยจุดเริ่มต้นคือ/fp:fastที่จับNaN's unsafely (ซึ่งยังผลในการNaN == NaNเป็นtrueตัวอย่าง) ลองระบุ/fp:strictหรือ/fp:preciseดูว่ามีประโยชน์หรือไม่


15
ฉันแค่ลองเอง แน่นอนว่าการระบุอย่างแม่นยำหรือแก้ไขปัญหาอย่างเข้มงวด
imallett

67
ฉันต้องการรับรองการตัดสินใจเริ่มต้นของ Intel /fp:fastหากคุณต้องการสิ่งที่ปลอดภัยคุณน่าจะหลีกเลี่ยง NaN ที่เปิดตัวตั้งแต่แรกและโดยทั่วไปจะไม่ใช้==กับหมายเลขทศนิยม อาศัยความหมายแปลก ๆ ที่ IEEE754 กำหนดให้กับ NaN กำลังถามถึงปัญหา
leftaroundabout

10
@leftaroundabout: คุณพบอะไรที่แปลกเกี่ยวกับ NaN นอกเหนือจากการตัดสินใจที่น่ากลัวของ IMHO ที่ให้ NaN! = NaN กลับมาจริงหรือ?
supercat

21
NaNs มีการใช้งานที่สำคัญ - พวกเขาสามารถตรวจจับสถานการณ์ที่ยอดเยี่ยมโดยไม่ต้องมีการทดสอบหลังจากการคำนวณทุกครั้ง ไม่ใช่นักพัฒนาจุดลอยตัวทุกคนที่ต้องการ แต่อย่ายกเลิก
Bruce Dawson

6
@supercat จากความอยากรู้คุณเห็นด้วยกับการตัดสินใจNaN==NaNกลับมาfalseไหม?
Kyle Strand

53

นี้ . . . ไม่ถูกใช่มั้ย คำถามของฉัน: มาตรฐานที่เกี่ยวข้อง (ISO C, ISO C ++, IEEE 754) พูดถึงอะไรได้บ้าง

Petr Abdulin ตอบแล้วทำไมคอมไพเลอร์ให้0.0คำตอบ

นี่คือสิ่งที่ IEEE-754: 2008 พูดว่า:

(6.2 Operations with NaNs) "[... ] สำหรับการดำเนินการที่มีอินพุต NaN แบบเงียบนอกเหนือไปจากการดำเนินการขั้นสูงสุดและขั้นต่ำหากผลลัพธ์จุดลอยตัวถูกส่งมอบผลลัพธ์จะเป็น NaN ที่เงียบซึ่งควรเป็นหนึ่งใน ใส่ NaN "

ดังนั้นผลลัพธ์ที่ถูกต้องเพียงอย่างเดียวสำหรับการลบตัวถูกดำเนินการ NaN สองตัวที่เงียบสงบคือตัวย่อที่เงียบ ผลลัพธ์อื่นใดไม่ถูกต้อง

The C Standard พูดว่า:

(C11, F.9.2 การแปลงนิพจน์ p1) "[... ]

x - x → 0. 0 "นิพจน์ x - x และ 0 0 ไม่เท่ากันถ้า x เป็น NaN หรืออนันต์"

(ที่นี่ NaN หมายถึงเงียบ NaN ตาม F.2.1p1 "ข้อมูลจำเพาะนี้ไม่ได้กำหนดพฤติกรรมของการส่งสัญญาณ NaNs โดยทั่วไปจะใช้คำว่า NaN เพื่อแสดง NaN ที่เงียบสงบ")


20

เมื่อฉันเห็นคำตอบที่เป็นไปตามมาตรฐานของคอมไพเลอร์ของ Intel และไม่มีใครพูดถึงเรื่องนี้ฉันจะชี้ให้เห็นว่าทั้ง GCC และ Clang มีโหมดที่พวกเขาทำสิ่งที่คล้ายกันมาก พฤติกรรมเริ่มต้นของพวกเขาเป็นไปตามมาตรฐาน IEEE -

$ g++ -O2 test.cc && ./a.out 
neg: -nan
sub: nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan

$ clang++ -O2 test.cc && ./a.out 
neg: -nan
sub: -nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan

- แต่ถ้าคุณขอความเร็วโดยใช้ความถูกต้องคุณจะได้สิ่งที่คุณขอ -

$ g++ -O2 -ffast-math test.cc && ./a.out 
neg: -nan
sub: nan nan 0.000000
add: nan nan
div: nan nan 1.000000
mul: nan nan

$ clang++ -O2 -ffast-math test.cc && ./a.out 
neg: -nan
sub: -nan nan 0.000000
add: nan nan
div: nan nan nan
mul: nan nan

ฉันคิดว่ามันยุติธรรมอย่างยิ่งที่จะวิจารณ์การเลือกค่าเริ่มต้นของ ICC แต่ฉันจะไม่อ่านสงคราม Unix ทั้งหมดกลับสู่การตัดสินใจ


ขอให้สังเกตว่าด้วย-ffast-math, gccไม่เป็นไปตามมาตรฐาน ISO 9899: 2011 ที่เกี่ยวกับเลขทศนิยมใด ๆ
fuz

1
@FUZxxl ใช่ประเด็นคือคอมไพเลอร์ทั้งคู่มีโหมดเลขทศนิยมที่ไม่สอดคล้องกันมันเป็นเพียงแค่ค่าเริ่มต้น icc ในโหมดนั้นและ gcc ไม่มี
zwol

4
เพียงเพื่อโยนเชื้อเพลิงเข้าไปในกองเพลิงฉันชอบทางเลือกของ Intel ในการเปิดใช้งานคณิตศาสตร์อย่างรวดเร็วตามค่าเริ่มต้น จุดทั้งหมดของการใช้ลอยตัวคือการได้รับปริมาณงานสูง
Navin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.