2015-06-16 65 views
5

Występuje problem z precyzją zmiennoprzecinkową w bibliotece dynamicznej.Czy plik wykonywalny w systemie Linux może wpływać na precyzję zmiennoprzecinkową w połączonej bibliotece dynamicznej?

set-up jest w następujący sposób:

  • Mamy dynamiczną bibliotekę, która wykonuje obliczeń X na dużej tablicy liczb zmiennoprzecinkowych. X składa się z wielu operacji zmiennoprzecinkowych.
  • Łączymy to dynamiczne biblioteki do dwóch plików wykonywalnych: A i B.
  • W bibliotece drukujemy wejście do obliczeń X.
  • dla obu systemem wykonywalnego A i B dokładnie to samo wejście jest zgłoszony (do DBL_DIG miejsca dziesiętne).
  • Wyjście obliczeń X jest jednak różna dla wykonywalnego A niż dla wykonywalnego B.

Oba pliki wykonywalne i biblioteki napisane w C++ i skompilowany na tym samym komputerze przy użyciu tego samego GCC wersję kompilatora . Biblioteka jest skompilowana tylko raz z tymi samymi ustawieniami kompilatora co pliki wykonywalne A, ale ustawienia kompilatora dla wykonywalnych B mogą być różne.

Ponieważ używana jest ta sama biblioteka, spodziewaliśmy się tej samej dokładności obliczeń dla obu plików wykonywalnych, pod warunkiem, że dostarczono to samo wejście. Wygląda na to, że precyzja zmiennoprzecinkowa biblioteki zależy od czynników zewnętrznych, np. specyficzne konfiguracje procesów.

Czy jest to możliwe, a jeśli tak, to w jaki sposób można uzyskać taką samą precyzję w obu przebiegach (program A i B)?

Edycja 1

udało mi się stworzyć minimalny przykład, który pokazuje różnice. Jeśli (jak obliczeń X powiedzmy) Używam następujący kod w bibliotece wyniki są różne dla obu przebiegów (A i B):

float* value = new float; 
*value = 2857.0f; 
std::cout << std::setprecision(15) << std::log(*value) << std::endl; 

ja również drukowane pływa w formacie binarnym i pokazują różnicę w ostatni bit.

Niestety nie można kontrolować całego łańcucha kompilacji plików wykonywalnych A. W rzeczywistości A jest ponownie biblioteką dynamiczną, która jest używana z innego pliku wykonywalnego, dla którego nie mogę kontrolować ani znać opcji kompilatora.

Próbowałem użyć wielu różnych opcji kompilatora optymalizacji na pliku wykonywalnym B, aby zobaczyć, czy mogę uzyskać takie same wyniki jak dla pliku wykonywalnego A, ale do tej pory nie rozwiązało to problemu.

Edycja 2

Wyjście asemblera z powyższym kodzie jest:

.LFB1066: 
    .cfi_startproc 
    .cfi_personality 0x9b,DW.ref.__gxx_personality_v0 
    push rbp # 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    push rbx # 
    .cfi_def_cfa_offset 24 
    .cfi_offset 3, -24 
    sub rsp, 8 #, 
    .cfi_def_cfa_offset 32 
    mov edi, 4 #, 
    call [email protected] # 
    mov DWORD PTR [rax], 0x45329000 #* D.23338, 
    mov rdi, QWORD PTR [email protected][rip] # tmp66, 
    mov rax, QWORD PTR [rdi] # cout._vptr.basic_ostream, cout._vptr.basic_ostream 
    mov rax, QWORD PTR -24[rax] # tmp68, 
    mov QWORD PTR 8[rax+rdi], 15 # <variable>._M_precision, 
    movsd xmm0, QWORD PTR .LC1[rip] #, 
    call [email protected] # 
    mov rbx, rax # D.23465, 
    mov rax, QWORD PTR [rax] # <variable>._vptr.basic_ostream, <variable>._vptr.basic_ostream 
    mov rax, QWORD PTR -24[rax] # tmp73, 
    mov rbp, QWORD PTR 240[rbx+rax] # D.23552, <variable>._M_ctype 
    test rbp, rbp # D.23552 
    je .L9 #, 
    cmp BYTE PTR 56[rbp], 0 # <variable>._M_widen_ok 
    je .L5 #, 
    movsx esi, BYTE PTR 67[rbp] # D.23550, <variable>._M_widen 

Edycja 3

Jak sugerowano w komentarzach ja drukowanych zarówno pływających tryb punktu zaokrąglania i Informacje o statusie SSE w bibliotece.

W obu seriach (wykonywalny A i B) dostaję te same wartości:

tryb
  • zaokrągleń: 895
  • SSE cywilny: 8114
+1

Czy jeden z plików binarnych jest kompilowany z wysokim poziomem optymalizacji? Niektóre poziomy mogą powodować niebezpieczną matematykę lub modyfikować precyzję domyślną. Zobacz: https://gcc.gnu.org/wiki/FloatingPointMath dla listy przełączników, które zmieniają zachowanie operacji zmiennoprzecinkowych w GCC. – Matthew

+0

Muszę to sprawdzić. Wrócę do tego jutro. Ale czy to oznacza, że ​​ustawienia kompilacji pliku wykonywalnego mogą wpływać na precyzję w już skompilowanej bibliotece współdzielonej? –

+0

Tak, jeśli opcja zmienia domyślną szerokość lub zachowanie obliczeniowe. Spróbuj wydrukować rzeczywiste bajty zmiennych w bibliotece i nie używaj printf, aby sprawdzić, czy są one poprawnie reprezentowane, i użyj '-ffloat-store' we wszystkich kompilacjach, aby upewnić się, że szerokość pozostaje taka sama. Na wstępie powinieneś wymusić '-00' na wszystkich kompilacjach, aby sprawdzić, czy to rozwiązuje problem. – Matthew

Odpowiedz

1

Odpowiedź na Twoje pytanie brzmi: tak, w zasadzie proces może zmienić kontekst zmiennoprzecinkowy, w ramach którego działa twój kod.


o danym kodzie i wartości:

The trybu zaokrąglania (jak sugeruje Matteo) mogłyby wpłynąć ciąg formatowania jak to wielokrotnie dzieli przez 10 - ale nie mogę odtworzyć problemu przy użyciu std::fesetround .

Nie widzę również, jak to naprawdę wpłynęłoby na wzór bitowy, który według Ciebie był inny. Kod złożenia pokazuje literał 0x45329000, który jest równoważny 2857.0, a sam literał nie może być zmieniony przez zmiennoprzecinkowe env.

+0

Nie jestem pewien, czy to doda informację, ale zauważ, że podczas przesyłania strumieniowego do couta wykonuję dziennik o wartości 2857. Wynik tego dziennika różni się. Wartość 2857 nie wydaje się. –

Powiązane problemy