16

Someone told me that:

Under x86-64, FP arithmetic is done with SSE, and therefore long double is 64 bits.

But in the x86-64 ABI it says that:

C type sizeof alignment AMD64 Architecture
long double 16 16 80-bit extended (IEEE-754)

See: amd64-abi.pdf

and gcc says sizeof(long double) is 16 and gives FLT_DBL = 1.79769e+308 and FLT_LDBL = 1.18973e+4932

So I'm confused, how is long double 64 bit? I thought it is an 80-bit representation.

phuclv
  • 37,963
  • 15
  • 156
  • 475
Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319
  • Re: "FLT_DBL = 1.79769e+308 and FLT_LDBL = 1.18973e+4932": there is a typo. It should be DBL_MAX = 1.79769e+308 and LDBL_MAX = 1.18973e+4932. – pmor Jul 25 '23 at 09:55

3 Answers3

14

Under x86-64, FP arithmetic is done with SSE, and therefore long double is 64 bits.

That's what usually happens under x86-64 (where the presence of SSE instructions is guaranteed), but the program is still free to use x87, to which the compiler can resort when you use a long double.

You can have confirmation of this by compiling a program like this with g++ on Linux:

#include <iostream>
#include <cstdlib>
#include <ctime>

int main()
{
    std::srand(std::time(NULL));
    float f1=rand(), f2=rand();
    double d1=rand(), d2=rand();
    long double l1=rand(), l2=rand();

    std::cout<<f1*f2<<" "<<d1*d2<<" "<<l1*l2<<std::endl;
    return 0;
}

In the assembly output, I find mulsd xmm1, xmm0 for the double product and mulss xmm0, xmm2 for the float product (both SSE instructions), but fmulp st(1), st (x87 instruction) for the long double product.

So, it's confirmed, the compiler uses SSE when it can, but still allows 80-bit precision computations via the old x87 instruction set.


Notice that this is compiler-specific - some compilers (e.g. VC++) always ignored 80-bit precision types and just treated long double as a synonym of double.

On the other hand, since the x86-64 System V ABI (adopted on Linux) mandates that long double is 80 bit, the only way for a compiler to perform computations using all the available precision of the type is to use the x87 instructions.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • *"Notice that this is compiler-specific"* - Which one did you use when you inspected the disassembly quoted in this answer? *"VC++ always ignored 80-bit precision types"* - That's not correct. Up to and including Visual Studio 6, the `long double` data type used the 80-bit extended representation. – IInspectable Jul 21 '16 at 17:50
  • @IInspectable: heh, who knows, it's been three years; probably whatever was included in the Ubuntu-derivative I was using at the time, it may be gcc 4.6. Anyhow, a quick test (with a simpler, [clearer test case](https://godbolt.org/g/TgQr9a)) shows that the generated code is virtually identical with all the x86_64 compilers available (`mulsd` for `double`, `mulss` for `float`, `fmulp` for `long double`). – Matteo Italia Jul 22 '16 at 07:09
  • @IInspectable: for VC++, my reference probably was https://msdn.microsoft.com/en-us/library/9cx8xs15(v=vs.71).aspx, which says that all 32 bit VC++ versions ignored 80-bit `long double`: «Previous 16-bit versions of Microsoft C/C++ and Microsoft Visual C++ supported the `long double`, 80-bit precision data type. In Win32 programming, however, the `long double` data type maps to the `double`, 64-bit precision data type.». I'm sure this is the case in VC++ 2002 and later, however I don't have a VC++ <=6 installation to test this with earlier versions. – Matteo Italia Jul 22 '16 at 07:10
  • Any idea why, if `long double` caluclations are FPU/80-bit, `sizeof(long double)` is 16 and not 10? I run into this problem when passing data between C and Fortran code with `KIND=10`; the storage does not match then. – Daniel Langr Aug 03 '20 at 14:44
  • 1
    @DanielLangr: I seem to recall that the last 6 bytes are unused and just for padding / alignment. – Andrew Tomazos Jul 30 '21 at 01:02
5

The AMD ABI cannot actually compel what the C long double type is, because it does not have the jurisdiction/authority to do so. Each C implementation can make its own specification of what the types are, subject to the C standard (if the C implementation is conforming to the standard), and each C implementation can choose whether or not to conform to the AMD ABI.

This means you cannot simply ask “What is long double on x86-64?” You must ask what is long double in a specific C implementation. That means specifying a particular C compiler, version, and switches used to compile. (A compiler might have a switch for which one setting makes long double a 64-bit binary IEEE 754-floating-point object and another setting makes long double the 80-bit Intel floating-point object. Technically, each different combination of compiler, version, and switches is a distinct C implementation.)

A compiler that implements long double as a 64-bit binary IEEE-754 floating-point object simply passes them as what the AMD ABI calls double; it never passes them as what the ABI calls long double. In doing so, this aspect of the compiler would be compatible only with other software that treats long double similarly.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 2
    Your wording is a little odd. It is necessary for userland applications to agree with the kernel about the size, alignment and representation of types - otherwise system calls won't work. That is the function of the ABI. In that sense the ABI "compels" the applications to obey it, because if they don't, they won't work (at least when they try to make system calls). – Andrew Tomazos Mar 03 '13 at 18:22
  • @user1131467: (1) A given kernel may choose to not adhere to the AMD ABI. (2) On some platforms, system calls are abstracted by a user-space library provided with the C environment, which can patch up any such impedance mismatches. – Stephen Canon Mar 03 '13 at 18:43
  • 4
    @StephenCanon: Yes, strictly speaking it isn't the "AMD ABI" or the "x86-64 ABI", it is the "System V x86-64 ABI". Linux chooses to adhere to it, Windows doesn't. – Andrew Tomazos Mar 03 '13 at 19:11
  • The argument works for the C standard as well, each C implementation can make its own specification, the standard cannot compell otherwise as well. – Remember Monica Sep 26 '22 at 10:08
  • @AndrewTomazos: The ABI specifies only binary interfaces. It has no jurisdiction over what type the type name `long double` maps to. It only says things like, if a routine using the ABI is called and is passed an eight-byte floating-point value, then where the value is passed. – Eric Postpischil Sep 26 '22 at 10:16
0

The SSE calculation is double precision and the intermediate representation is 64 bits.

That's not long double, as you have pointed out. The 64 bit value is just written to the long double after calculation.

justin
  • 104,054
  • 14
  • 179
  • 226
  • I didn't follow you sorry. If I use the `long double` type, is the arithmetic performed with 80-bit precision? – Andrew Tomazos Mar 02 '13 at 15:50
  • @user1131467 expanded. you will have to look at the asm for a definitive answer for a given set of build settings. the compiler could use either the FPU (80-bit) or SSE2 (64-bit). – justin Mar 02 '13 at 15:53
  • I decompiled addition, it is using `FADDP` and the `%st` and `%st(1)` register for example. That looks like fpu I think. – Andrew Tomazos Mar 02 '13 at 15:57