Search code examples
gccfloating-pointx86precisionx87

How does GCC compile the 80 bit wide 10 byte float __float80 on x86_64?


According to one of the slides in the video by What's A Creel video, "Modern x64 Assembly 4: Data Types" (link to the slide),

Note: real10 is only used with the x87 FPU, it is largely ignored nowadays but offers amazing precision!

He says,

"Real10 is only used with the x87 Floating Point Unit. [...] It's interesting the massive gain in precision that it offers you. You kind of take a performance hit with that gain because you can't use real10 with SSE, packed, SIMD style instructions. But, it's kind of interesting because if you want extra precision you can go to the x87 style FPU. Now a days it's almost never used at all."

However, I was googling and saw that GCC supports __float80 and __float128.

Is the __float80 in GCC calculated on the x87? Or it is using SIMD like the other float operations? What about __float128?


Solution

  • GCC docs for Additional Floating Types:

    ISO/IEC TS 18661-3:2015 defines C support for additional floating types _Floatn and _Floatnx

    ... GCC does not currently support _Float128x on any systems.

    I think _Float128x is IEEE binary128, i.e. a true 128-bit float with a huge exponent range. See http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1691.pdf.


    __float80 is obviously the x87 10-byte type. In the x86-64 SysV ABI, it's the same as long double; both have 16-byte alignment in that ABI.

    __float80 is available on the i386, x86_64, and IA-64 targets, and supports the 80-bit (XFmode) floating type. It is an alias for the type name _Float64x on these targets.


    I think __float128 is an extended-precision type using SSE2, presumably a "double double" format with twice the mantissa width but the same exponent limits as 64-bit double. (i.e. less exponent range than __float80)

    On i386, x86_64, and ..., __float128 is an alias for _Float128

    Those are probably the same doubledouble that gcc gives you with __float128. Or maybe it's a pure software floating point 128-bit


    Godbolt compiler explorer for gcc7.3 -O3 (same as gcc4.6, apparently these types aren't new)

    //long double add_ld(long double x) { return x+x; }  // same as __float80
    __float80 add80(__float80 x) { return x+x; }
    
        fld     TBYTE PTR [rsp+8]    # arg on the stack
        fadd    st, st(0)
        ret                          # and returned in st(0)
    
    
    __float128 add128(__float128 x) { return x+x; }
    
              # IDK why not movapd or better movaps, silly compiler
        movdqa  xmm1, xmm0           # x arg in xmm0
        sub     rsp, 8               # align the stack
        call    __addtf3             # args in xmm0, xmm1
        add     rsp, 8
        ret                          # return value in xmm0, I assume
    
    
    int size80 = sizeof(__float80);    // 16
    int sizeld = sizeof(long double);  // 16
    
    int size128 = sizeof(__float128);  // 16
    

    So gcc calls a libgcc function for __float128 addition, not inlining an increment to the exponent or anything clever like that.