Neden Intel C ++ Derleyici ile NaN - NaN == 0.0?


300

NaN'lerin aritmetik olarak yayıldığı iyi bilinir, ancak herhangi bir gösteri bulamadım, bu yüzden küçük bir test yazdım:

#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;
}

Örnek ( burada canlı yayın ) temelde beklediğim şeyi üretir (negatif biraz garip, ama mantıklı):

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

MSVC 2015 benzer bir şey üretir. Ancak Intel C ++ 15 şunları üretir:

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

Özellikle qNaN - qNaN == 0.0,.

Bu ... doğru olamaz, değil mi? İlgili standartlar (ISO C, ISO C ++, IEEE 754) bu konuda ne söylüyor ve derleyiciler arasında davranışta neden bir fark var?


18
Javascript ve Python (numpy) bu davranışa sahip değildir. Nan-NaNolduğunu NaN. Perl ve Scala da benzer şekilde davranıyor.
Paul

33
Güvenli olmayan matematik optimizasyonlarını ( -ffast-mathgcc'ye eşdeğer ) etkinleştirmiş olabilirsiniz ?
Matteo Italia

5
@nm: Doğru değil. Belirtilen kayan nokta davranışı için desteklenir ve gerektiğinde isteğe bağlıdır, ancak normatif Ek F, hiç , esas olarak C geri IEEE 754 içerir
R .. GitHub DUR YARDIMCI ICE

5
IEEE 754 standardı hakkında soru sormak isterseniz, sorunun bir yerinde belirtin.
n. 'zamirler' m.

68
Ben emin bu soru başlığından JavaScript ilgiliydi.
MikeTheLiar

Yanıtlar:


300

Intel C ++ derleyicisindeki varsayılan kayan nokta işleme /fp:fast, bunların NaNgüvenli olmayan şekilde işlenmesidir (bu da örneğin NaN == NaNolma ile sonuçlanır true). Belirtmeyi deneyin /fp:strictveya /fp:precisebunun işe yarayıp yaramadığını görün.


15
Bunu kendim deniyordum. Gerçekten, kesin veya katı bir şekilde belirtmek sorunu çözer.
imallett

67
Intel'in varsayılan kararını onaylamak istiyorum /fp:fast: Eğer güvenli bir şey istiyorsanız , NaN'lerin ilk etapta ortaya çıkmasından kaçınmalısınız ve genellikle ==kayan nokta sayılarıyla kullanmayın. IEEE754'ün NaN'ye atadığı garip semantiğe güvenmek sorun istiyor.
leftaroundabout

10
@ leftaroundabout: IMHO'nun NaN! = NaN'ın geri dönmesine ilişkin korkunç kararının yanı sıra NaN hakkında tuhaf buluyor musunuz?
supercat

21
NaN'lerin önemli kullanımları vardır - her hesaplamadan sonra test gerektirmeden istisnai durumları tespit edebilirler. Her kayan noktalı geliştiricinin bunlara ihtiyacı yoktur, ancak reddetmez.
Bruce Dawson

6
@supercat Meraktan NaN==NaNgeri dönme kararını kabul ediyor falsemusunuz?
Kyle Strand

53

Bu . . . doğru olamaz, değil mi? Sorum: ilgili standartlar (ISO C, ISO C ++, IEEE 754) bu konuda ne diyor?

Petr Abdulin derleyicinin neden 0.0cevap verdiğini zaten yanıtladı .

İşte IEEE-754: 2008 şöyle diyor:

(6.2 NaN ile İşlemler) "[...] Sessiz NaN girişleri olan bir işlem için, maksimum ve minimum işlemler dışında, bir kayan noktalı sonuç iletilecekse, sonuç, aşağıdakilerden biri olması gereken sessiz bir NaN olacaktır. giriş NaNs. "

Dolayısıyla, iki sessiz NaN işlenmesinin çıkarılması için tek geçerli sonuç sessiz bir NaN'dir; başka bir sonuç geçerli değil.

C Standardı diyor ki:

(C11, F.9.2 İfade dönüşümleri p1) "[...]

x - x → 0. 0 "x bir NaN veya sonsuz ise x - x ve 0. 0 ifadeleri eşdeğer değildir"

(burada NaN, F.2.1p1 "Bu spesifikasyon NaN'leri sinyalleme davranışını tanımlamaz. Genellikle sessiz NaN'leri belirtmek için NaN terimini kullanır" uyarınca sessiz bir NaN'yi belirtir))


20

Intel derleyicisinin standartlara uygunluğunu uyaran bir cevap gördüğümden ve başka hiç kimse bundan bahsetmediğinden, hem GCC hem de Clang'ın oldukça benzer bir şey yaptıkları bir moda sahip olduklarına dikkat çekeceğim. Varsayılan davranışları IEEE uyumludur -

$ 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

- ama doğruluk pahasına hız istersen, istediğini alırsın -

$ 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

Bence ICC'nin temerrüt seçimini eleştirmek tamamen adil , ama tüm Unix savaşlarını bu karara geri okumam.


Bildirim o -ffast-math, gccISO 9899 ile uyumlu değildir: bir daha kayar nokta aritmetik göre, 2011.
fuz

1
@FUZxxl Evet, nokta, her iki derleyicinin de uyumsuz bir kayan nokta moduna sahip olması, sadece icc'nin bu moda varsayılan olması ve gcc'nin olmamasıdır.
zwol

4
Sadece ateşe yakıt atmak için, Intel'in varsayılan olarak hızlı matematiği etkinleştirmesini tercih ediyorum. Şamandıra kullanmanın tüm amacı yüksek verim elde etmektir.
Navin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.