Soru sinüs sonucu kullanılan C ++ derleyicisine bağlıdır


Aşağıdaki iki C ++ derleyiciyi kullanırım:

  • cL.exe : Microsoft (R) C / C ++ Derleyici İyileştirme 19.00.24210 Versiyonu x86 için
  • g ++ : g ++ (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010

Yerleşik sinüs fonksiyonunu kullanırken farklı sonuçlar elde ederim. Bu kritik değildir, ancak bazen kullanımım için sonuçlar çok önemlidir. İşte 'kodlanmış' bir değere sahip bir örnek:

printf("%f\n", sin(5451939907183506432.0));

Cl.exe ile sonuç:

0.528463

G ++ ile sonuç:

0.522491

G ++ 'nin sonucunun daha doğru olduğunu ve aynı sonucu elde etmek için ek bir kütüphane kullanabileceğimi biliyorum, ama bu benim amacım değil. Burada ne olduğunu gerçekten anlayacağım. Neden bu cl.exe yanlış?

Komik şey, paramda bir modulo (2 * pi) uygularsam, o zaman g ++ 'dan aynı sonucu elde ederim ...

[DÜZENLE] Sadece benim örneğim bazılarınız için çıldırdığı için: bu bir sözde sayı üretecinin bir parçasıdır. Sinüs sonucunun doğru olup olmadığını bilmek önemli değildir: sadece bir sonuç vermek için ihtiyacımız var.


37
2017-10-12 13:46


Menşei


X86 komut kümesindeki bir göz, donanımda uygulanan FSIN ve FCOS matematik yönergelerini bulur, böylece sonuçların derleyici bağımsız olmasını beklersiniz. Belki de FSIN / FCOS'un büyük değerler ile yanlış olduğu biliniyor ve gcc ekstra mil gidiyor, ve mandarin FSIN / FCOS'u çalıştırmadan önce moduloyu uyguluyor. - Sam Varshavchik
Benimkide kalsın sin(x+2pi) eşittir sin(x). Pratikte, bu argüman anlamına gelir sin önceden ölçeklendirilir sin işlevi, [0..2pi] aralığında olmalıdır. Tartışma ne kadar büyük olursa, düşük bitlerde daha az önem vardır. Ön-ölçekleme ile düşük bitlerin çıkarılması, argüman büyüdükçe kesinlik kazanır. Esasen, büyük olan bir argümanın günahını hesaplamaya çalıştığınızda, sonuç saçmalıktır. Fonksiyon, gürültünün günahını hesaplamaya çalışıyor. Çöp içeri çöp dışarı. - Pete Becker
Her durumda hangi derleyici anahtarını geçiriyorsunuz? Optimizasyonlarınız etkin mi? Hassas seçenekler ile kontrol edilebilir. X87 FSIN ve FCOS talimatları burada kırmızı bir ringa balığı. SSE2 talimatları ile değiştirilen modern derleyiciler tarafından kullanılmıyorlar. GCC ile optimizasyonlar devre dışı bırakılmış olsa bile, bunu hesaplar. Derleme zamanı ve sonucu bir literal olarak tükürür. MSVC yapmayacak. - Cody Gray♦
Günahın rasgele üreteci için bu şekilde kullanılması çok kötü bir fikirdir. Yapma. - geza
İki kez, "Dua, Bay Babbage, makineye yanlış rakamlar koyduysanız, doğru cevaplar çıkacak mı?" Diye sordum. ... Böyle bir soruyu provoke edebilecek fikirlerin kafa karışıklığını doğru bir şekilde anlayamıyorum. - Charles Babbage - Arne Vogel


Cevaplar:


Bence Sam'in yorumu işarete en yakın. GCC / glibc'nin güncel bir sürümünü kullanıyorsunuz, bu da yazılımdaki sin () 'i (söz konusu değişmez için derleme zamanında hesaplanır) uygular. X86 için cl.exe muhtemelen fsin komutunu kullanır. İkincisi, Random ASCII blog gönderisinde açıklandığı gibi çok kesin olmayabilir. "Intel, 1,3 Quintillion'a Göre Hata Altını Aştı".

Özellikle örneğinizle ilgili sorunun bir kısmı, Intel'in kesin olmayan bir yaklaşımı kullanmasıdır. pi sayısı menzil azaltma yaparken:

Çift duyarlıktan (53 bit mantis) menzil azaltımı yapılırken pi sayısı Sonuçlar 2 ^ 40 ULP'ye (53 eksi 13) kadar bir hata için yaklaşık 13 bit hassasiyete (66 eksi 53) sahip olacaktır.


16
2017-10-12 17:34



Derleyicinin gcc'de bu hesaplamayı yaptığını ve cl ile çalışmasının, kod tarafından çalışma zamanında yapılmasının çok önemli olduğunu düşünüyorum. Biliyorum gcc, derleyicinin ürettiği kodla tam olarak aynı cevapları aldığından emin olmak için çok çaba harcadı. Ama bu, onları böyle konularda dikkatlice düşünmeye zorladı ve ben de aynı şeyi yapmadığından şüpheliyim. - Omnifarious
Blog yayınının "adil" olduğunu bile belirten hassas gereksinim mi? Karşılaştırma için, üç anlamlı basamağa sahip kayan noktalarla çalıştığımızı varsayalım. Sonra pi sayısı "3.14E0" ile gösterilir, ancak 3.135 ile 3.145 arasındaki herhangi bir sayıdır, böylece herhangi "-3.40E-3" ve "+ 6.59E-3" ("0" dahil) arasındaki çıkış, en yakın gösterilebilen değeri "3.14" olan bazı x için sin (x) 'nin en yakın gösterilebilir değeri olacaktır. Günahın (3.14) çıktısının "1.59E-3" olması gerektiğine inanmak bana göre ... - Hagen von Eitzen
"x86 için cl.exe muhtemelen fsin talimatını kullanır" Bu çok popüler bir teori gibi görünüyor, ama kesinlikle yanlış. Açıkça ifade etmedikçe /arch:IA32 çevirmek devre dışı SSE2 desteği, MSVC'nin 32 bit sürümü varsayar SSE2 desteği mevcuttur ve SSE2 talimatlarını üretir. Şimdi birçok versiyon için bunu yapmıştır. Özellikle, için sinkütüphane işlevini çağırır ___libm_sse2_sin veya __libm_sse2_sin_preciseayarladığınıza bağlı olarak /fp:fast veya /fp:precise (ikincisi varsayılan). Bu kullanımların hiçbiri FSIN (ne de FCOS ne de FSINCOS). - Cody Gray♦
@Cody Grey: Windows SDK 7.0 (80x86 için cl.exe sürümü 15.00.30729.01) ile 32-bit Windows 2003 sisteminde bunu test ettim. sin () ve fsin her ikisi de Nicolas'ın asıl soruya yazdığı aynı cevabı verir. - SloopJon
Derleyicinin @SloopJon Sürüm 15'i "modern" bir versiyon olarak adlandırdığım şey değil. Bu, bugüne kadar yaklaşık 10 yaşında olan VS 2008 ile birlikte gönderildi. VS 2012 (cl.exe sürüm 17), konuştuğum değişikliği tanıttığı sürüm, SSE2'yi 32 bitlik yapılarda bile desteklenen minimum destek kümesi olarak hedefliyor. Askerin 19'uncu sürümünü kullandığını unutmayın, o yüzden /arch:SSE2 x86-32 için varsayılan olarak ayarlanmışsa, özellikle bunu devre dışı bırakmadığı sürece /arch:IA32. Bu yüzden derleyici anahtarlarını orijinal olarak sordum. - Cody Gray♦


19 basamaklı bir yazım var, ancak çift genellikle 15-17 basamaklı bir kesinliğe sahip. Sonuç olarak, küçük bir göreli hata (çift dönüştürürken) elde edebilirsiniz, ancak büyük (sinüs hesaplama bağlamında) mutlak hatası alabilirsiniz.

Aslında standart kütüphanenin farklı uygulamaları, bu gibi büyük sayıların tedavisinde farklılıklar göstermektedir. Örneğin, çevremde, eğer yürütürsek

std::cout << std::fixed << 5451939907183506432.0;

g ++ sonuç olurdu 5451939907183506432.000000
cl sonuç olurdu 5451939907183506400.000000

Fark, 19'dan önceki cl sürümlerinin, yalnızca sınırlı sayıda rakam kullanan ve kalan ondalık basamakları sıfır ile dolduran bir biçimlendirme algoritmasına sahip olmalarıdır.

Dahası, bu koda bakalım:

double a[1000];
for (int i = 0; i < 1000; ++i) {
    a[i] = sin(5451939907183506432.0);
}
double d = sin(5451939907183506432.0);
cout << a[500] << endl;
cout << d << endl; 

X86 VC ++ derleyicimle çalıştırıldığında çıktı şu şekildedir:

0.522491
0.528463

Diziyi doldururken görünür sin çağrısına derlenmiştir __vdecl_sin2ve tek bir işlem olduğunda, çağrıya derlenir. __libm_sse2_sin_precise (ile /fp:precise).

Benim düşünceme göre, numaranız çok büyük sin Aynı davranışı farklı derleyicilerden beklemek ve genel olarak doğru davranışı beklemek için hesaplama.


36
2017-10-12 13:58



Derleyicilerin, bu değişmezi yorumlamadan aynı bit desenini üreteceğini düşünürdüm, o zaman sonuçlardaki farkı açıklayamayacaktı. - SirGuy
@SirGuy Muhtemelen bu standart tarafından belirtilmemiş ve mevcut yuvarlama ayarları ve derleyici bayraklarına tabi olarak çalışıyor. - lisyarus
@SirGuy, Evet, bu doğru. Aynı ikili değere sahipler. - DAle
Çünkü libstdc ++ değeri temsil edildiği gibi göstermeye çalışır (yani n.000000 n, 2) gücüne yuvarlanır. Öte yandan, MSVCRT sonunda sıfırları gösterecektir. GNU uygulaması tartışmalı bir şekilde daha doğrudur, ancak pratik amaçlar için bunlar her durumda önemli olmayan rakamlardır ve bunlara güvenilemez. - Arne Vogel
Ne etkisi std::fixed değerin kendisinde mi? - Ron


Göre cppreference:

Arg büyüklüğü büyükse sonuç çok az olabilir veya hiç önemi olmayabilir.   (C ++ 11'e kadar)

Sorunun sebebi bu olabilir, bu durumda moduloyu manuel olarak yapmak isteyeceksiniz. arg büyük değil.


9
2017-10-12 14:00



Moduloyu el ile yapmanın yardımcı olacağını sanmıyorum. - Joshua
@Joshua neden olmasın? Bir derleyici modulo ve diğerini yaparsa, sonuçlardaki farkı açıklayamaz. - SirGuy
Çünkü x86 işlemci, modülün altında bile uzun çift sinüs fonksiyonu için yanlış bir şey yapar. Gerçekten test ettiğiniz şey, bu libc'in bu hatayı daha iyi düzeltmesidir. - Joshua
@joshua modülün altında bile yanlış olan şey nedir? Sizden mi bahsediyorsunuz SloopJohn'un cevabı ve bağlantılı makaleye? - SirGuy
Evet, sorun bu. Intel'e takıl. - Joshua