SORU
15 Temmuz 2013, PAZARTESİ


Bir belge kayan nokta aşırı hassas işler nasıl tarif var mı?

Neredeyse imkansız(*) izin verilen kayan nokta sadece talimatları kullanılan 387 olanlar ne zaman makul bir maliyetle katı IEEE 754 semantiği sağlar. Tek DEĞERLERİ long double türü genişletilmiş hassasiyet için kullanılabilir, böylece tam 64-bit significand çalışmaya devam etmek istediğinde özellikle zordur. “Çözüm” sadece mevcut hassas, daha fazla veya daha az aynı zamanda düşük bir hassasiyetle dönüştürmek için iyi tanımlanmış durumlarda Ara hesaplamaları yap. her zamanki

GCC yeni sürümleri yorumu 2008 post to the GCC mailing list Joseph S. Myers tarafından konulan Ara hesaplamaları aşırı hassas işlemek. Bu açıklama bir program gcc -std=c99 -mno-sse2 -mfpmath=387 ile derlenmiş tamamen öngörülebilir, son bit, anladığım kadarıyla yapar. Ve tesadüfen eğer doğru değilse, bir hata olduğunu ve sabit olacak değil: Joseph S. Myers' görevinden de belirtildiği gibi bir niyeti tahmin edilebilir hale getirmektir.

Aşırı hassas ki seçeneği -mno-sse2 kullanıldığında () işler nasıl, nerede ve belgelenmiş mi?

(*) EDİT: bu bir abartma. slightly annoying but not that difficult 53-bit significand bir kullanmak için x 87 bu DEĞERLERİ yapılandırmak için izin verildiğinde binary64 taklit.


R.. aşağıda yorum şu, burada İse en son sürümü ile benim kısa bir etkileşim günlüğü var :

Hexa:~ $ clang -v
Apple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn)
Target: x86_64-apple-darwin12.4.0
Thread model: posix
Hexa:~ $ cat fem.c
#include <stdio.h>
#include <math.h>
#include <float.h>
#include <fenv.h>

double x;
double y = 2.0;
double z = 1.0;

int main(){
  x = y   z;
  printf("%d\n", (int) FLT_EVAL_METHOD);
}
Hexa:~ $ clang -std=c99 -mno-sse2 fem.c
Hexa:~ $ ./a.out 
0
Hexa:~ $ clang -std=c99 -mno-sse2 -S fem.c
Hexa:~ $ cat fem.s 
…
    movl    $0, %esi
    fldl    _y(%rip)
    fldl    _z(%rip)
    faddp   %st(1)
    movq    _x@GOTPCREL(%rip), %rax
    fstpl   (%rax)
…

CEVAP
24 EYLÜL 2013, Salı


Bu aslında yöneltilen soruya cevap vermez, ama eğer bir programcı benzer sorunlar ile çalışıyorsanız, bu cevap size yardımcı olabilir.

Gerçekten algılanan zorluk nerede olduğunu göremiyorum. Sağlayan katı IEEE-754 binary64 semantik olurken sınırlı 80387 kayan nokta matematik ve istinat 80 bitlik long double hesaplama, gibi takip de belirtilen C99 döküm kuralları ile hem GCC-4.6.3 ve çınlama-3.0 (temel LLVM 3.0).

Eklemek için düzenlenebilir: Henüz, Pascal Cuoq doğrudur: ne-gcc 4.6.3 ya-llvm-3.0 çınlama aslında bu kuralları uygulamakdoğru'387 kayan nokta matematik. Uygun derleyici seçenekleri göz önüne alındığında, kuralları doğru ifadeler çalışma zamanı ifadeler için derleme zamanında, ama değerlendirilmesi için uygulanır. Geçici çözümler aradan sonra aşağıda listelenmiştir.

Ben moleküler dinamik simülasyon Kodu, ve ben çok aşina tekrarlama/öngörülebilirlik şartları ve aynı zamanda arzu korumak için maksimum hassasiyet mevcut zaman mümkün, ben iddia biliyorum neden bahsettiğimi burada. Bu cevabı araçları var olduğunu göstermeli ve kullanmak için basit, sorunlar ya da bu araçları kullanarak değil, farkında olmadığım ortaya çıkar.

(Tercih edilen bir örnek, ben gibi, Kahan toplamı algoritması. C99 ve uygun döküm (Wikipedia kod örneği, örneğin atmalarını ekleme), herhangi bir hile ya da başka geçici değişkenler hiç ihtiyaç vardır. Uygulama ne olursa olsun derleyici optimizasyon seviyesi işleri, -O3 -Ofast. de dahil olmak üzere)

C99 döküm ve atama fazladan aralığı kaldırmak ve hassas her ikisi de açıkça (örneğin 5.4.2.2) Birleşik Devletleri. Bu demektir kullanabileceğiniz long double aritmetik tanımlayarak senin geçici değişkenleri sırasında kullanılan hesaplama long double, ayrıca döküm giriş değişkenleri için bu tür; her bir IEEE-754 binary64 gerekli, sadece oyuncular için double.

'387, dökme bir görev ve hem de yukarıdaki Derleyiciler üzerinde bir yük oluşturuyor; bu 80 bit değeri IEEE-754 binary64 yuvarlak mı doğru. Bu maliyet bence çok makul. Tam olarak ne zaman çekilmiş mimarisi ve çevre kodu bağlıdır; genellikle ve neglible seviyelere maliyeti getirmek için başka bir kod ile aralanmış olabilir. MMX, SSE veya AVX mevcut olduğunda, onların kayıtlarına 80-bit 80387 kayıtları ayrı, alçı genellikle BÖYLE/SSE/AVX kayıt değeri taşıyarak yapılır.

(Tercihim üretim kodu kullanın belirli bir kayan nokta türü, söyle tempdouble ya da böyle, geçici değişkenler olması tanımlanan ya double long double bağlı olarak mimarlık ve hız/hassas bileşimleri istenilen.)

Özetle:

(expression) tüm değişkenler ve edebi değeri vardır diye double hassas olduğunu düşünmeyin. Eğer double hassas sonuç istiyorsanız (double)(expression) olarak yaz.

Bu ifadeler bileşik için de geçerlidir, ve bazen atmalarını birçok seviyeleri ile, kaba ifadeler neden olabilir.

Eğer 80-bit hassas hesaplamak, ama aynı zamanda 64-bit için her yuvarlak ürünü ilk ihtiyacımız istediğiniz expr1 expr2 varsa, kullanın

long double  expr1;
long double  expr2;
double       product = (double)(expr1) * (double)(expr2);

Not iki 64-bit bir ürün olarak hesaplanan değerleri;değil80-bit hassas hesaplanmış, daha sonra aşağı yuvarlanır. 80-bit hassas ürün hesaplama, yuvarlama, olacaktır

double       other = expr1 * expr2;

ya da, tam olarak ne oluyor, söyle de sözlerine ekledi açıklayıcı atmalarını

double       other = (double)((long double)(expr1) * (long double)(expr2));

product other sık sık farklı açık olmalı.

C99 döküm kuralları eğer karma ile 32-bit/64-bit/80-bit/128-bit kayan nokta değerleri eğer ellerinde, öğrenmek gerekir başka bir araçtır. Gerçekten, eğer sen binary32 ve binary64 mix (en mimarilerifloat double) yüzer eğer aynı sorunlarla karşılaşabilirsiniz!

Belki Pascal Cuoq keşif kod yeniden yazma, doğru döküm kuralları uygulamak için, bu daha net yapar?

#include <stdio.h>

#define TEST(eq) printf("%-56s%s\n", "" # eq ":", (eq) ? "true" : "false")

int main(void)
{
    double d = 1.0 / 10.0;
    long double ld = 1.0L / 10.0L;

    printf("sizeof (double) = %d\n", (int)sizeof (double));
    printf("sizeof (long double) == %d\n", (int)sizeof (long double));

    printf("\nExpect true:\n");
    TEST(d == (double)(0.1));
    TEST(ld == (long double)(0.1L));
    TEST(d == (double)(1.0 / 10.0));
    TEST(ld == (long double)(1.0L / 10.0L));
    TEST(d == (double)(ld));
    TEST((double)(1.0L/10.0L) == (double)(0.1));
    TEST((long double)(1.0L/10.0L) == (long double)(0.1L));

    printf("\nExpect false:\n");
    TEST(d == ld);
    TEST((long double)(d) == ld);
    TEST(d == 0.1L);
    TEST(ld == 0.1);
    TEST(d == (long double)(1.0L / 10.0L));
    TEST(ld == (double)(1.0L / 10.0));

    return 0;
}

Çıktı, hem de GCC ve çınlama ile

sizeof (double) = 8
sizeof (long double) == 12

Expect true:
d == (double)(0.1):                                     true
ld == (long double)(0.1L):                              true
d == (double)(1.0 / 10.0):                              true
ld == (long double)(1.0L / 10.0L):                      true
d == (double)(ld):                                      true
(double)(1.0L/10.0L) == (double)(0.1):                  true
(long double)(1.0L/10.0L) == (long double)(0.1L):       true

Expect false:
d == ld:                                                false
(long double)(d) == ld:                                 false
d == 0.1L:                                              false
ld == 0.1:                                              false
d == (long double)(1.0L / 10.0L):                       false
ld == (double)(1.0L / 10.0):                            false

bunun dışında son sürüm GCC teşvik sağ tarafına ld == 0.1 uzun çift (yani ld == 0.1L), getirili true ve SSE/AVX, long double 128-bit.

Saf '387 testleri, kullandım

gcc -W -Wall -m32 -mfpmath=387 -mno-sse ... test.c -o test
clang -W -Wall -m32 -mfpmath=387 -mno-sse ... test.c -o test

-fomit-frame-pointer, -O0, -O1, -O2, -O3, ve -Os dahil ... gibi çeşitli optimizasyon bayrak kombinasyonu ile.

Başka bir bayrak veya C99 Derleyiciler kullanarak long double boyut dışında aynı sonuçlar için, (ve geçerli GCC sürümleri için ld == 1.0) öncülük etmelidir. Sen herhangi bir farklılık karşılaşırsanız, çok minnettar onları duymak isterim; bu tür Derleyiciler (sürüm derleyici) benim kullanıcıları uyarmak gerekebilir. Microsoft bana son derece ilginç yani C99, desteklemediğini unutmayın.


Pascal Cuoq hemen tanıyamadım hangi yorum zinciri ilginç bir sorun, aşağıda getirin.

Bir ifadeyi değerlendirirken, -mfpmath=387 ile GCC ve çınlama tüm ifadelerin 80-bit duyarlıklı olarak değerlendirilen belirtin. Bu örnek için yol açar

7491907632491941888 = 0x1.9fe2693112e14p 62 = 110011111111000100110100100110001000100101110000101000000000000
5698883734965350400 = 0x1.3c5a02407b71cp 62 = 100111100010110100000001001000000011110110111000111000000000000

7491907632491941888 * 5698883734965350400 = 42695510550671093541385598890357555200 = 100000000111101101101100110001101000010100100001011110111111111111110011000111000001011101010101100011000000000000000000000000

verimli sonuçlar yanlış, çünkü o dize olanlar ortasında ikili sonucudur sadece arasındaki fark 53 - ve 64-bit mantissas (64 ve 80-bit kayan nokta sayıları, sırasıyla). Beklenen sonuç ise yani,

42695510550671088819251326462451515392 = 0x1.00f6d98d0a42fp 125 = 100000000111101101101100110001101000010100100001011110000000000000000000000000000000000000000000000000000000000000000000000000

sonuç -std=c99 -m32 -mno-sse -mfpmath=387 ile elde edilir

42695510550671098263984292201741942784 = 0x1.00f6d98d0a43p 125 = 100000000111101101101100110001101000010100100001100000000000000000000000000000000000000000000000000000000000000000000000000000

Teoride, gcc ve çınlama seçenekleri kullanarak doğru C99 yuvarlama kuralları uygulamak için söylemek gerekir

-std=c99 -m32 -mno-sse -mfpmath=387 -ffloat-store -fexcess-precision=standard

Ancak, bu sadece derleyici optimize ifadeler etkiler ve 387 taşıma düzeltmek için hiç görünmüyor. Kullanırsanız, örneğin clang -O1 -std=c99 -m32 -mno-sse -mfpmath=387 -ffloat-store -fexcess-precision=standard test.c -o test && ./test 64* Pascal Cuoq's example program, sen-ecek almak doğru sonucu başına IEEE-754 kuralları -- ama sadece çünkü derleyici en iyi duruma getirir uzak bir ifade kullanarak, 387.

Basitçe, yerine bilgisayar koymak

(double)d1 * (double)d2

hem çınlama aslında gcc söyle 'hesaplamak için 387

(double)((long double)d1 * (long double)d2)

Bu gerçekten deBu derleyici bir hata gcc-4.6.3 ve çınlama-llvm-3.0 ve kolayca yeniden bir de etkiliyor. (Pascal Cuoq çekiyor FLT_EVAL_METHOD=2 gelir işlemleri üzerinde çift duyarlıklı değişkenler her zaman yaptığı genişletilmiş hassas, ama göremiyorum ki aklı başında bir sebebi dışında olması için yeniden parça libm '387 -- bunu yapmak içinde C99 ve dikkate IEEE-754 kuralları başarılabilir tarafından donanım! Tüm sonradoğruoperasyon kolayca '387 ifade hassasiyetle maç için kelime kontrol. değiştirerek derleyici tarafından ulaşılabilir. Ve, derleyici seçenekleri göz önüne alındığındagerekirbu *69 -- * -- FLT_EVAL_METHOD=2 davranış aslında istenirse hiçbir mantıklı davranış kuvvet, geriye uyumluluk sorunları da yoktur.) Önemli uygun derleyici bayrakları verilen ifadeler derleme zamanında değerlendirilmesi doğru değerlendirilmesi yapmak unutmayın, ve sadece ifadeleri çalışma zamanında değerlendirilmesi hatalı sonuçlar elde.

En basit çözüm, taşınabilir, fesetround(FE_TOWARDZERO) (fenv.h) sıfıra doğru tüm sonuçları ve tur için kullanmaktır.

Bazı durumlarda, sıfıra doğru yuvarlama öngörülebilirlik ve patolojik durumları ile yardımcı olabilir. x = [0,1) doğru sıfır demektir yuvarlama gibi aralıkları için özellikle, üst sınırı asla eğer örneğin eğriler değerlendirmek parçalı ise; önemli yuvarlama yoluyla ulaştı.

Başka yuvarlama modları için, 387 donanımı doğrudan kontrol etmek gerekir.

#include <fpu_control.h>, __FPU_SETCW() ya da açık kodu da kullanabilirsiniz. , 76**: örneğin

#include <stdlib.h>
#include <stdio.h>
#include <limits.h>

#define FP387_NEAREST   0x0000
#define FP387_ZERO      0x0C00
#define FP387_UP        0x0800
#define FP387_DOWN      0x0400

#define FP387_SINGLE    0x0000
#define FP387_DOUBLE    0x0200
#define FP387_EXTENDED  0x0300

static inline void fp387(const unsigned short control)
{
    unsigned short cw = (control & 0x0F00) | 0x007f;
    __asm__ volatile ("fldcw %0" : : "m" (*&cw));
}

const char *bits(const double value)
{
    const unsigned char *const data = (const unsigned char *)&value;
    static char buffer[CHAR_BIT * sizeof value   1];
    char       *p = buffer;
    size_t      i = CHAR_BIT * sizeof value;
    while (i-->0)
        *(p  ) = '0'   !!(data[i / CHAR_BIT] & (1U << (i % CHAR_BIT)));
    *p = '\0';
    return (const char *)buffer;
}


int main(int argc, char *argv[])
{
    double  d1, d2;
    char    dummy;

    if (argc != 3) {
        fprintf(stderr, "\nUsage: %s 7491907632491941888 5698883734965350400\n\n", argv[0]);
        return EXIT_FAILURE;
    }

    if (sscanf(argv[1], " %lf %c", &d1, &dummy) != 1) {
        fprintf(stderr, "%s: Not a number.\n", argv[1]);
        return EXIT_FAILURE;
    }
    if (sscanf(argv[2], " %lf %c", &d2, &dummy) != 1) {
        fprintf(stderr, "%s: Not a number.\n", argv[2]);
        return EXIT_FAILURE;
    }

    printf("%s:\td1 = %.0f\n\t    %s in binary\n", argv[1], d1, bits(d1));
    printf("%s:\td2 = %.0f\n\t    %s in binary\n", argv[2], d2, bits(d2));

    printf("\nDefaults:\n");
    printf("Product = %.0f\n\t  %s in binary\n", d1 * d2, bits(d1 * d2));

    printf("\nExtended precision, rounding to nearest integer:\n");
    fp387(FP387_EXTENDED | FP387_NEAREST);
    printf("Product = %.0f\n\t  %s in binary\n", d1 * d2, bits(d1 * d2));

    printf("\nDouble precision, rounding to nearest integer:\n");
    fp387(FP387_DOUBLE | FP387_NEAREST);
    printf("Product = %.0f\n\t  %s in binary\n", d1 * d2, bits(d1 * d2));

    printf("\nExtended precision, rounding to zero:\n");
    fp387(FP387_EXTENDED | FP387_ZERO);
    printf("Product = %.0f\n\t  %s in binary\n", d1 * d2, bits(d1 * d2));

    printf("\nDouble precision, rounding to zero:\n");
    fp387(FP387_DOUBLE | FP387_ZERO);
    printf("Product = %.0f\n\t  %s in binary\n", d1 * d2, bits(d1 * d2));

    return 0;
}

Çınlama-llvm-3.0 doğru sonuçlar elde derlemek ve çalıştırmak için kullanarak

clang -std=c99 -m32 -mno-sse -mfpmath=387 -O3 -W -Wall precision.c -o precision
./precision 7491907632491941888 5698883734965350400

7491907632491941888:    d1 = 7491907632491941888
        0100001111011001111111100010011010010011000100010010111000010100 in binary
5698883734965350400:    d2 = 5698883734965350400
        0100001111010011110001011010000000100100000001111011011100011100 in binary

Defaults:
Product = 42695510550671098263984292201741942784
          0100011111000000000011110110110110011000110100001010010000110000 in binary

Extended precision, rounding to nearest integer:
Product = 42695510550671098263984292201741942784
          0100011111000000000011110110110110011000110100001010010000110000 in binary

Double precision, rounding to nearest integer:
Product = 42695510550671088819251326462451515392
          0100011111000000000011110110110110011000110100001010010000101111 in binary

Extended precision, rounding to zero:
Product = 42695510550671088819251326462451515392
          0100011111000000000011110110110110011000110100001010010000101111 in binary

Double precision, rounding to zero:
Product = 42695510550671088819251326462451515392
          0100011111000000000011110110110110011000110100001010010000101111 in binary

Diğer bir deyişle, fp387() hassas ve yuvarlama modunu ayarlamak için kullanarak derleyici sorunlara çözüm bulabilirsiniz.

Dezavantajı bazı matematik kitaplıkları (, ** 80 ** 81) Ara sonuçlar her zaman 80-bit hassas hesaplanır varsayımı ile yazılmış olabilir. X86_64 en azından GNU C kütüphanesi fpu_control.h yorum< . em ^"libm genişletilmiş hassasiyet gerektirir". Neyse ki, alabilirsin '387 uygulamaları-örneğin GNU C kütüphanesi, uygulamak ve onları bir başlık dosyası yazma bilinen bir iş libm eğer ihtiyacın olursa math.h işlevsellik; aslında, sanırım yardımcı olabilirsin.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Joshua Bane

    Joshua Bane

    24 Temmuz 2007
  • Chaîne de TheMoustic

    Chaîne de T

    5 Kasım 2006
  • WestsideMrArO

    WestsideMrAr

    6 EKİM 2010