<p>Intel created a series of floating-point coprocessors for the x86 called the x87. In addition to supporting IEEE single and double precision numbers, it also supported an 80-bit extended precision number. Some C compilers (e.g. clang) mapped this to the <code>long double</code> type in C, but others (e.g. MSVC) didn't.</p>
<p>Many languages have no built-in support for this type. The most recent example I know of that does is Swift, which has a <code>Float80</code> type only available when compiling for Intel processors. (Swift also has <code>CLongDouble</code> which represents the exact type that the C compiler takes <code>long double</code> to mean, which is sometimes the same thing as <code>Double</code>.) The only time I've seen <code>Float80</code> or <code>long double</code> used in practice is to use the increased precision to emulate a <a href="https://en.wikipedia.org/wiki/Multiply%E2%80%93accumulate_operation" rel="noreferrer">fused multiply-add</a> instruction on older processors that don't support it, or very rarely to avoid loss of precision when converting from a 64-bit integer.</p>
<p>Evidently support for this type isn't strictly <em>necessary</em>, since so many languages get by without it, but why would a modern language choose to support or not support the type?</p>

<floating-point>
Are x87 long doubles still relevant?
CC BY-SA 4.0
2023-07-15T15:54:18.283Z
[Related question from Stack Overflow](https://stackoverflow.com/questions/26280229/is-x87-fp-stack-still-relevant)
dan04
CC BY-SA 4.0
2023-07-15T22:22:14.197Z
There's a logical extension of this question that asks about *all* processor-specific data types, such as SIMD types.
Criticize SE actions means ban
CC BY-SA 4.0
2023-07-16T08:50:46.330Z
As an aside, on arm64, long double is 128-bit (and implemented in software).
jpa
CC BY-SA 4.0
2023-07-16T14:00:37.250Z
Yes, even on Windows, GCC and clang apparently default to 80-bit `long double`, unlike MSVC.  They have options `-mlong-double-64/80/128` to change the ABI, making `long double` a different type, allowing compat with MSVC for example.  Also, see [Did any compiler fully use Intel x87 80-bit floating point?](https://retrocomputing.stackexchange.com/a/9760) re: the (in)efficiency of 80-bit FP load/store on modern x86 (quite a bit slower than load/store with conversion from/to double or float)
Peter Cordes
CC BY-SA 4.0
2023-07-17T11:10:58.523Z
C doesn't have 80 bit floating point types. It has `float`, `double` and `long double`, anyone of this may or may not use 80 bit. If you want a language similar to C, you need to support types with minimum requirements that may have different actually features on different platforms.
12431234123412341234123
CC BY-SA 4.0
2023-07-17T18:14:17.310Z
@12431234123412341234123 I never claimed that any type was specifically 80 bits. In fact, I said quite the opposite, that some compilers never supported 80-bit floating-point types in the first place.
Bbrk24
CC BY-SA 4.0
2023-07-18T08:36:11.880Z
@Bbrk24 Yes, sorry, i should have said it has nothing to do with the compiler. gcc nor clang maps `long double` to a 80 bit double, except on x86 and AMD64.
12431234123412341234123
<blockquote>
<p>Evidently support for this type isn't strictly necessary, since so many languages get by without it, but why would a modern language choose to support or not support the type?</p>
</blockquote>
<p>A modern language may choose to support the <em>notion</em> of it. In other words, the language may have built-in support for a <code>Float0</code> type, representing the more precise native hardware floating point available.<sup>1</sup></p>
<p>The <code>0</code> suffix means that it has not a predefined size, but it is a floating point anyways. It can be converted between other number types in the number tower, and its bits could be converted from or into a <code>byte[]</code>.</p>
<p>The marketing is easy: <em><code>Float0</code>, the <strike>best</strike> wider floating point representation that the language can find in your computer.</em></p>
<p><sup>1. Or the software emulation configured in compiler parameters.</sup></p>

CC BY-SA 4.0
2023-07-18T15:13:25.100Z
2023-08-01T14:56:33.663Z
2023-08-01T14:56:33.663Z
André L F S Bacci
<blockquote>
<p>Are x87 long doubles still relevant?</p>
</blockquote>
<p>No. The x64 instruction set <a href="https://learn.microsoft.com/en-us/windows/win32/dxtecharts/sixty-four-bit-programming-for-game-developers#porting-applications-to-64-bit-platforms" rel="nofollow noreferrer">deprecated the x87 FPU</a> in favour of SSE for doing floating point math:</p>
<blockquote>
<blockquote>
<p>The x87, MMX, and 3DNow! instruction sets are deprecated in 64-bit modes. The instructions sets are still present for backward compatibility for 32-bit mode; however, to avoid compatibility issues in the future, their use in current and future projects is discouraged.</p>
</blockquote>
</blockquote>
<p>This means that, for x64 applications, there is no guarantee the x87 instruction set will be present.</p>
<blockquote>
<p>In addition to supporting IEEE single and double precision numbers, it also supported an 80-bit extended precision number. Some C compilers (e.g. clang) mapped this to the long double type in C</p>
</blockquote>
<p>It may be worth noticing that the C language standard is intentionally vague in defining the type for exactly this issue you're seeing.</p>
<p><code>double</code> must have greater or equal precision as <code>float</code>. At no point it says one must be 64-bit and the other 32-bit precision.</p>
<p>It is also worth remembering that the x87 FPU had no ability to store the  80 bits into memory. Those extra 16 bits only lived in registers and were lost once they spill into memory. Its usefulness has always been limited.</p>
<blockquote>
<p>why would a modern language choose to support or not support the type?</p>
</blockquote>
<p>You will have to ask the compiler implementor why they included it.</p>
<p>Writing for Legacy is a thing. There are industries still using Windows XP for their QA software/hardware and some banks still run SW written in COBOL for Mainframes in the 70's.</p>
<p>Why they'd chose to use old SW/HW instead of modern ones needs to be evaluated on a case by case basis, but often the reason boils down to one of these three:</p>
<ol>
<li>&quot;It's too expensive to migrate everything, so we paid someone to add functionality we needed to our current compiler&quot;</li>
<li>&quot;We got that HW in a bargain&quot;</li>
<li>&quot;Don't replace what ain't broken&quot;</li>
</ol>

CC BY-SA 4.0
2023-07-16T19:29:15.647Z
2023-07-16T19:29:15.647Z
Matias N Goldberg
CC BY-SA 4.0
2023-07-17T01:22:09.107Z
x86-64 didn't officially deprecate x87; *Microsoft* did.  Maybe that's what you meant since you called it "x64", Microsoft's name for it.  Intel and AMD haven't deprecated it.  x86-64 does make SSE2 baseline, guaranteeing that a more efficient way to do scalar math is available, so x87 is obsolete but not deprecated in the sense of pending removal.  Even [Intel's proposed x86S (which removes 16-bit mode)](https://www.intel.com/content/www/us/en/developer/articles/technical/envisioning-future-simplified-architecture.html) doesn't propose removing it from the hardware
Peter Cordes
CC BY-SA 4.0
2023-07-17T01:30:51.823Z
Rumors of Windows x64 not supporting MMX or x87 floating point are greatly exaggerated.  MSVC [removed MMX intrinsics](https://stackoverflow.com/questions/60933486/mmx-intrinsics-like-mm-cvtpd-pi32-not-found-with-msvc-2019-for-64bit-targets-c), but the kernel's context-switch code still saves/restores x87 state so machine code made with other toolchains continues to work fine.
Peter Cordes
CC BY-SA 4.0
2023-07-17T01:32:52.063Z
Despite what https://learn.microsoft.com/en-us/windows/win32/dxtecharts/sixty-four-bit-programming-for-game-developers#porting-applications-to-64-bit-platforms about future systems possibly not supporting MMX or x87, I find it implausible that future Windows would set control-register bits so x87 and MMX instructions faulted instead of working in 64-bit user-space.  Intel and AMD are very unlikely to remove them in the forseeable future, although they may continue to get a bit slower as dedicated HW support for them is removed to simplify other parts of the CPU.
Peter Cordes
CC BY-SA 4.0
2023-07-17T01:37:16.573Z
Some major software like FFmpeg's software decoder for h.264 (AVC) video uses hand-written MMX instructions, including in 64-bit mode, so it would break backwards compat with some commercially-relevant software; something vendors go to great lengths to avoid.  Perhaps relevant, the SSE3 feature bit (CPUID) indicates support for [x87 `fisttp`](https://www.felixcloutier.com/x86/fisttp) as well as a bunch of XMM instructions, although a hypothetical x86 without x87 could just not support that either.
Peter Cordes
CC BY-SA 4.0
2023-07-17T01:40:15.313Z
*the x87 FPU had no ability to store the 80 bits into memory.* - Not true: `fstp m80` and `fld m80` are valid (but significantly slower than m32/m64). https://www.felixcloutier.com/x86/fst:fstp . `fst m80` isn't valid, so you'd need to duplicate it first with `fld st0` if you want a copy left in the x87 stack after storing.  And you can't use an 80-bit long double as a memory source operand for any math instruction, e.g. `fadd m32/m64` are valid but not `fadd m80`.  See Nate's answer here and mine on [Did any compiler fully use x87 80-bit FP?](//retrocomputing.stackexchange.com/q/9751)
Peter Cordes
CC BY-SA 4.0
2023-07-17T07:20:42.477Z
x87 isn't deprecated by Intel, but it is highly discouraged, and Intel has explicitly stated that it has performance issues that they will not fix. User/Source Coding Rule 9 from the *Intel 64 and IA-32 Architectures Optimization Reference Manual* states: "Enable the compiler’s use of Intel SSE, Intel SSE2, Intel AVX, Intel AVX2, and possibly more advanced SIMD instruction sets (Intel AVX-512) with appropriate switches. Favor scalar SIMD code generation to replace x87 code generation."
Pseudonym
<p>On many embedded platforms without floating-point units, computations using a 32-bit or 64-bit mantissa without an &quot;implied 1&quot; would be faster, more precise, and in just about every way better than those IEEE-754 64-bit double-precision values.  Unfortunately, the way the C Standard added <code>long double</code> broke a key aspect of the language: that all floating-point values passed to variadic functions be converted to a common type.</p>
<p>If you're designing a language for use with embedded systems that may lack floating-point unit, I'd suggest having the following floating-point types:</p>
<ul>
<li><code>float</code>: typically 32 bits total, unless configured to be a different size.</li>
<li><code>long float</code>: type to which <code>float</code> values are promoted for computations (and might be as small as <code>float</code> or as big as <code>long double</code>.</li>
<li><code>double</code>: typically 64 bits total, unless configured to be a different size.</li>
<li><code>long double</code>: type to which <code>double</code> values are promoted for computations.</li>
</ul>
<p>I would then suggest having a means of explicitly passing types other than <code>double</code> to functions, but say that expressions that don't explicitly force the type of a floating-point value passed to a variadic function would by default be converted to <code>double</code>.</p>
<p>On many platforms, operations on 64-bit floating-point values would require unpacking the mantissa to a 64-bit combination of registers, performing computations with a 64-bit mantissa, and then packing the result.  Processing an expression like <code>a+b+c</code> by unpacking <code>a</code> and <code>b</code>, adding them, normalizing and packing the result, and then unpacking that result along with <code>c</code>, adding them, and finally normalizing and packing that result, would take more work and yield less precise results than omitting the operations that would normalize and pack and then unpack the result from the original computation.</p>
<p>Extended floating-point types are sufficiently uncommon that most programmers wouldn't particularly favor them for their improved semantics, but if a set of development tools for an embedded system can perform floating-point calculations which are both faster and more precise than its competitors, that would be a nice advantage.</p>

CC BY-SA 4.0
2023-07-16T17:05:11.000Z
2023-07-18T08:30:46.080Z
2023-07-18T08:30:46.080Z
supercat
CC BY-SA 4.0
2023-07-16T17:32:16.743Z
I would hope that a new language, if it is going to have variadic functions, would support them in a better way than the afterthought hack that is `<stdarg.h>` (and its predecessor `<varargs.h>`).  So hopefully the type conversion issue wouldn't arise at all.
Nate Eldredge
CC BY-SA 4.0
2023-07-16T17:42:15.780Z
@NateEldredge: Even if a language has better calling convention for its own variadic functions (I could offer some ideas, if you'd want to hear them), it would still be useful to support interoperability with other languages that use the C calling convention for such functions.
supercat
CC BY-SA 4.0
2023-07-16T17:44:29.780Z
@supercat Swift supports calling C variadic functions via [`withVaList`](https://developer.apple.com/documentation/swift/withvalist(_:_:)). I’ll note that it only supports calling e.g. `vprintf` and not `printf` in this manner.
Bbrk24
CC BY-SA 4.0
2023-07-16T17:56:04.870Z
@NateEldredge: I wish that compound literals had been specified as yielding static const lvalues when all values were compile-time constants, and non-l-values otherwise, but there was a convenient syntax to take a non-l-value, copy it to a temporary, and yield the address thereof, in sitautions where the resulting pointer is used in a dereferencing or function call expression.
supercat
<p>I can see several reasons to <em>not</em> bother supporting it.</p>
<ul>
<li><p>It's not supported by many processors other than x86.  So if your language is going to be cross-platform, what do you do when building code for another platform?  You could map it to float64, but if the programmer is using it, presumably it's because they really wanted the extra precision.  You could provide software emulation, but that's a lot of trouble and will have unsatisfactory performance.  You could map it to float128, but there are few if any FPUs with native float128 support so you are back to software emulation again.</p>
</li>
<li><p>It is only supported by x87 floating point, which is itself painful for a compiler writer.  The stack-like register layout is awkward for register allocation and code generation. Until recently, gcc had a <a href="https://github.com/gcc-mirror/gcc/blob/releases/gcc-11.4.0/gcc/reg-stack.c" rel="noreferrer"> 3000-line source file</a> in its &quot;machine independent&quot; layer, dedicated to handling register stacks, which AFAIK only ever applied to the x87.</p>
<p>And since it's a &quot;legacy&quot; instruction set, modern CPUs don't tend to optimize x87 instructions very well.  If you don't need float80, then you have the option to do all your x86 floating point with SSE, which is a much more &quot;normal&quot; architecture with a random-access register file (<code>xmm</code>), and ignore the x87 altogether.  SSE is supported by all x86-64 CPUs, and by all 32-bit x86 CPUs from the last 20 years or so.</p>
</li>
<li><p>Even within x87, using float80 is more awkward than other types.  It seems clear that the x87 design assumed that programmers would primarily use float32 and float64; float80 was intended more for &quot;internal use&quot;, as an implementation detail to provide additional precision for intermediate results.  So while you can use float80 explicitly, it comes with some restrictions.  For instance, most two-operand x87 arithmetic instructions use the top of the floating-point stack <code>st(0)</code> as the implicit first operand, and the second can be either another <code>st</code> register, or else a float32 or float64 from memory - but not a float80.  For float80, you need an additional instruction to load it into a register first.  So you'll need more special cases in your code generation.</p>
</li>
</ul>
<p>See also:</p>
<ul>
<li><p><a href="https://stackoverflow.com/questions/26280229/is-x87-fp-stack-still-relevant">Is x87 FP stack still relevant?</a> on SO</p>
</li>
<li><p><a href="https://stackoverflow.com/questions/3206101/extended-80-bit-double-floating-point-in-x87-not-sse2-we-dont-miss-it?rq=3">Extended (80-bit) double floating point in x87, not SSE2 - we don't miss it?</a> on SO</p>
</li>
<li><p><a href="https://retrocomputing.stackexchange.com/questions/9751/did-any-compiler-fully-use-intel-x87-80-bit-floating-point">Did any compiler fully use Intel x87 80-bit floating point?</a> on Retrocomputing.SE for a historical perspective</p>
</li>
</ul>

CC BY-SA 4.0
2023-07-15T19:02:27.170Z
2023-07-16T01:25:27.970Z
2023-07-16T01:25:27.970Z
Nate Eldredge
CC BY-SA 4.0
2023-07-16T00:56:09.717Z
It´s not unique to x86. The Motorola 68881/68882 coprocessors support an 80-bit floating type as well.
d3jones
CC BY-SA 4.0
2023-07-16T14:09:43.340Z
x87 FP math instructions are not *bad* even on modern microarchitectures, and `fxchg` is still zero latency (although 2 uops since Haswell, up from 1).  It's 80-bit load/store being slow that are the real killers for `long double` performance.  Like `fld m80` being 10 uops on Zen 4 with 1 per 16 cycle throughput (up from 4c on Zen 3, vs. Ice Lake being 4 uops / 2c).  vs. 2/clock throughput for `fld m32/m64`.  And `fst m80` being 17 uops with 5c throughput on Zen 4, similar to Ice Lake's 5c throughput for several generations.
Peter Cordes
CC BY-SA 4.0
2023-07-16T17:49:05.257Z
@PeterCordes: Hmm, where are you finding this data?  uops.info doesn't seem to have info on x87 instructions for anything later than Skylake.  But on Skylake, for instance, `addsd` executes on ports 0 or 1, so it gets double the throughput of `fadd` which only executes on port 5.
Nate Eldredge
CC BY-SA 4.0
2023-07-16T17:56:41.327Z
Agner Fog's tables have up to Ice Lake.  Yes, `fadd` and `fmul` throughput are only 1/clock vs. 2/clock for SSE/AVX, but they are fully pipelined and 3 or 4 cycle latency respectively.  In code with an even mix of add and multiply, you have a best-case throughput of 2 FLOP/clock with SSE or x87.  In code with only one or the other, you have a factor of 2 advantage for SSE/AVX scalar math.  That's not great, but my point was that it's only truly **bad** when we look at `fld m80` / `fstp m80` performance being (much) worse than the 1/clock or 2/clock we get with SSE/AVX or x87 `m32` / `m64`.
Peter Cordes
CC BY-SA 4.0
2023-07-16T18:02:30.023Z
Other fun facts from flipping through the table: (1) Sandybridge made `fcomi` 3 uops, up from 1, as legacy x87 became less relevant.  (2) Skylake moved the x87 `fadd`/`fcom` unit to port 5 (previously port 1).  So Skylake and later can do 3/clock scalar FP adds: two with SSE on p01, and one with x87 on p5. (3)  `fmul` latency was 5 cycles until Ice lake; CPUs from Skylake to Cannon Lake has 5c `fmul` vs. 4c `mulsd`.
Peter Cordes
<p>The 8087 had 80-bit registers so that if the inputs to your computation had 64-bit accuracy, the outputs would also have 64-bit accuracy.</p>
<p>The way floating-point arithmetic was supposed to work, when IEEE 754 and the 8087 were designed, is that when you compute something like w ← a + bx + cyz, all of the intermediate values are computed at a higher precision than the inputs and outputs. This is similar to the best practice for hand calculation. People sometimes ask &quot;if I'm calculating a result to 3 sig figs, should I round all of the intermediates to 3 sig figs also?&quot; and the answer to that is no—not if you can avoid it. Keeping extra digits around helps to avoid cumulative accuracy loss from roundoff.</p>
<p>Extra digits make it easier for ordinary mortals to write floating-point calculations that won't go wrong for hard-to-analyze reasons. The more extra precision you have, the more you can imagine that your custom formulas (a + bx + cyz) will behave similarly to library functions (sin x) that were designed by experts to have a error of no more than 1 ulp over their whole domain. You don't get guaranteed accuracy, but you do get more reliable accuracy.</p>
<p>As someone who doesn't know any of the black magic needed to implement those library functions, but does know enough about floating point (and sig figs) to understand the value of the extra digits, I would really appreciate having more programming languages that let me use them. It's especially maddening when languages like CPython, which are so inefficient in both space and time that the extra precision would cost essentially nothing, still don't let me use it.</p>

CC BY-SA 4.0
2023-07-16T23:40:33.557Z
2023-07-17T00:24:10.670Z
2023-07-17T00:24:10.670Z
benrg
# Are x87 long doubles still relevant?
Posted by: username_0 on 2023-07-15
Intel created a series of floating-point coprocessors for the x86 called the x87. In addition to supporting IEEE single and double precision numbers, it also supported an 80-bit extended precision number. Some C compilers (e.g. clang) mapped this to the `long double` type in C, but others (e.g. MSVC) didn't.

Many languages have no built-in support for this type. The most recent example I know of that does is Swift, which has a `Float80` type only available when compiling for Intel processors. (Swift also has `CLongDouble` which represents the exact type that the C compiler takes `long double` to mean, which is sometimes the same thing as `Double`.) The only time I've seen `Float80` or `long double` used in practice is to use the increased precision to emulate a [fused multiply-add](https://en.wikipedia.org/wiki/Multiply%E2%80%93accumulate_operation) instruction on older processors that don't support it, or very rarely to avoid loss of precision when converting from a 64-bit integer.

Evidently support for this type isn't strictly *necessary*, since so many languages get by without it, but why would a modern language choose to support or not support the type?
**Tags:** floating-point
> **Comments:**
> **username_1**: [Related question from Stack Overflow](https://stackoverflow.com/questions/26280229/is-x87-fp-stack-still-relevant)  
> **username_2**: There's a logical extension of this question that asks about *all* processor-specific data types, such as SIMD types.  
> **username_3**: As an aside, on arm64, long double is 128-bit (and implemented in software).  
> **username_4**: Yes, even on Windows, GCC and clang apparently default to 80-bit `long double`, unlike MSVC.  They have options `-mlong-double-64/80/128` to change the ABI, making `long double` a different type, allowing compat with MSVC for example.  Also, see [Did any compiler fully use Intel x87 80-bit floating point?](https://retrocomputing.stackexchange.com/a/9760) re: the (in)efficiency of 80-bit FP load/store on modern x86 (quite a bit slower than load/store with conversion from/to double or float)  
> **username_5**: C doesn't have 80 bit floating point types. It has `float`, `double` and `long double`, anyone of this may or may not use 80 bit. If you want a language similar to C, you need to support types with minimum requirements that may have different actually features on different platforms.  
> **username_0**: @12431234123412341234123 I never claimed that any type was specifically 80 bits. In fact, I said quite the opposite, that some compilers never supported 80-bit floating-point types in the first place.  
> **username_5**: @Bbrk24 Yes, sorry, i should have said it has nothing to do with the compiler. gcc nor clang maps `long double` to a 80 bit double, except on x86 and AMD64.  
---
## 5 answers
### Answer by username_10 on 2023-07-15. Score: 21 | Accepted answer
I can see several reasons to *not* bother supporting it.

* It's not supported by many processors other than x86. So if your language is going to be cross-platform, what do you do when building code for another platform? You could map it to float64, but if the programmer is using it, presumably it's because they really wanted the extra precision. You could provide software emulation, but that's a lot of trouble and will have unsatisfactory performance. You could map it to float128, but there are few if any FPUs with native float128 support so you are back to software emulation again.
* It is only supported by x87 floating point, which is itself painful for a compiler writer. The stack-like register layout is awkward for register allocation and code generation. Until recently, gcc had a  [3000-line source file](https://github.com/gcc-mirror/gcc/blob/releases/gcc-11.4.0/gcc/reg-stack.c) in its "machine independent" layer, dedicated to handling register stacks, which AFAIK only ever applied to the x87.

  And since it's a "legacy" instruction set, modern CPUs don't tend to optimize x87 instructions very well. If you don't need float80, then you have the option to do all your x86 floating point with SSE, which is a much more "normal" architecture with a random-access register file (`xmm`), and ignore the x87 altogether. SSE is supported by all x86-64 CPUs, and by all 32-bit x86 CPUs from the last 20 years or so.
* Even within x87, using float80 is more awkward than other types. It seems clear that the x87 design assumed that programmers would primarily use float32 and float64; float80 was intended more for "internal use", as an implementation detail to provide additional precision for intermediate results. So while you can use float80 explicitly, it comes with some restrictions. For instance, most two-operand x87 arithmetic instructions use the top of the floating-point stack `st(0)` as the implicit first operand, and the second can be either another `st` register, or else a float32 or float64 from memory - but not a float80. For float80, you need an additional instruction to load it into a register first. So you'll need more special cases in your code generation.

See also:

* [Is x87 FP stack still relevant?](https://stackoverflow.com/questions/26280229/is-x87-fp-stack-still-relevant) on SO
* [Extended (80-bit) double floating point in x87, not SSE2 - we don't miss it?](https://stackoverflow.com/questions/3206101/extended-80-bit-double-floating-point-in-x87-not-sse2-we-dont-miss-it?rq=3) on SO
* [Did any compiler fully use Intel x87 80-bit floating point?](https://retrocomputing.stackexchange.com/questions/9751/did-any-compiler-fully-use-intel-x87-80-bit-floating-point) on Retrocomputing.SE for a historical perspective
> **Comments:**
> **username_11** (2023-07-16): It´s not unique to x86. The Motorola 68881/68882 coprocessors support an 80-bit floating type as well.  
> **username_4** (2023-07-16): x87 FP math instructions are not *bad* even on modern microarchitectures, and `fxchg` is still zero latency (although 2 uops since Haswell, up from 1).  It's 80-bit load/store being slow that are the real killers for `long double` performance.  Like `fld m80` being 10 uops on Zen 4 with 1 per 16 cycle throughput (up from 4c on Zen 3, vs. Ice Lake being 4 uops / 2c).  vs. 2/clock throughput for `fld m32/m64`.  And `fst m80` being 17 uops with 5c throughput on Zen 4, similar to Ice Lake's 5c throughput for several generations.  
> **username_10** (2023-07-16): @PeterCordes: Hmm, where are you finding this data?  uops.info doesn't seem to have info on x87 instructions for anything later than Skylake.  But on Skylake, for instance, `addsd` executes on ports 0 or 1, so it gets double the throughput of `fadd` which only executes on port 5.  
> **username_4** (2023-07-16): Agner Fog's tables have up to Ice Lake.  Yes, `fadd` and `fmul` throughput are only 1/clock vs. 2/clock for SSE/AVX, but they are fully pipelined and 3 or 4 cycle latency respectively.  In code with an even mix of add and multiply, you have a best-case throughput of 2 FLOP/clock with SSE or x87.  In code with only one or the other, you have a factor of 2 advantage for SSE/AVX scalar math.  That's not great, but my point was that it's only truly **bad** when we look at `fld m80` / `fstp m80` performance being (much) worse than the 1/clock or 2/clock we get with SSE/AVX or x87 `m32` / `m64`.  
> **username_4** (2023-07-16): Other fun facts from flipping through the table: (1) Sandybridge made `fcomi` 3 uops, up from 1, as legacy x87 became less relevant.  (2) Skylake moved the x87 `fadd`/`fcom` unit to port 5 (previously port 1).  So Skylake and later can do 3/clock scalar FP adds: two with SSE on p01, and one with x87 on p5. (3)  `fmul` latency was 5 cycles until Ice lake; CPUs from Skylake to Cannon Lake has 5c `fmul` vs. 4c `mulsd`.  
---
### Answer by username_12 on 2023-07-16. Score: 12
The 8087 had 80-bit registers so that if the inputs to your computation had 64-bit accuracy, the outputs would also have 64-bit accuracy.

The way floating-point arithmetic was supposed to work, when IEEE 754 and the 8087 were designed, is that when you compute something like w ← a + bx + cyz, all of the intermediate values are computed at a higher precision than the inputs and outputs. This is similar to the best practice for hand calculation. People sometimes ask "if I'm calculating a result to 3 sig figs, should I round all of the intermediates to 3 sig figs also?" and the answer to that is no—not if you can avoid it. Keeping extra digits around helps to avoid cumulative accuracy loss from roundoff.

Extra digits make it easier for ordinary mortals to write floating-point calculations that won't go wrong for hard-to-analyze reasons. The more extra precision you have, the more you can imagine that your custom formulas (a + bx + cyz) will behave similarly to library functions (sin x) that were designed by experts to have a error of no more than 1 ulp over their whole domain. You don't get guaranteed accuracy, but you do get more reliable accuracy.

As someone who doesn't know any of the black magic needed to implement those library functions, but does know enough about floating point (and sig figs) to understand the value of the extra digits, I would really appreciate having more programming languages that let me use them. It's especially maddening when languages like CPython, which are so inefficient in both space and time that the extra precision would cost essentially nothing, still don't let me use it.
---
### Answer by username_6 on 2023-07-18. Score: 4
> Evidently support for this type isn't strictly necessary, since so many languages get by without it, but why would a modern language choose to support or not support the type?

A modern language may choose to support the *notion* of it. In other words, the language may have built-in support for a `Float0` type, representing the more precise native hardware floating point available.1

The `0` suffix means that it has not a predefined size, but it is a floating point anyways. It can be converted between other number types in the number tower, and its bits could be converted from or into a `byte[]`.

The marketing is easy: *`Float0`, the best wider floating point representation that the language can find in your computer.*

1. Or the software emulation configured in compiler parameters.
---
### Answer by username_7 on 2023-07-16. Score: 3
> Are x87 long doubles still relevant?

No. The x64 instruction set [deprecated the x87 FPU](https://learn.microsoft.com/en-us/windows/win32/dxtecharts/sixty-four-bit-programming-for-game-developers#porting-applications-to-64-bit-platforms) in favour of SSE for doing floating point math:

> > The x87, MMX, and 3DNow! instruction sets are deprecated in 64-bit modes. The instructions sets are still present for backward compatibility for 32-bit mode; however, to avoid compatibility issues in the future, their use in current and future projects is discouraged.

This means that, for x64 applications, there is no guarantee the x87 instruction set will be present.

> In addition to supporting IEEE single and double precision numbers, it also supported an 80-bit extended precision number. Some C compilers (e.g. clang) mapped this to the long double type in C

It may be worth noticing that the C language standard is intentionally vague in defining the type for exactly this issue you're seeing.

`double` must have greater or equal precision as `float`. At no point it says one must be 64-bit and the other 32-bit precision.

It is also worth remembering that the x87 FPU had no ability to store the 80 bits into memory. Those extra 16 bits only lived in registers and were lost once they spill into memory. Its usefulness has always been limited.

> why would a modern language choose to support or not support the type?

You will have to ask the compiler implementor why they included it.

Writing for Legacy is a thing. There are industries still using Windows XP for their QA software/hardware and some banks still run SW written in COBOL for Mainframes in the 70's.

Why they'd chose to use old SW/HW instead of modern ones needs to be evaluated on a case by case basis, but often the reason boils down to one of these three:

1. "It's too expensive to migrate everything, so we paid someone to add functionality we needed to our current compiler"
2. "We got that HW in a bargain"
3. "Don't replace what ain't broken"
> **Comments:**
> **username_4** (2023-07-17): x86-64 didn't officially deprecate x87; *Microsoft* did.  Maybe that's what you meant since you called it "x64", Microsoft's name for it.  Intel and AMD haven't deprecated it.  x86-64 does make SSE2 baseline, guaranteeing that a more efficient way to do scalar math is available, so x87 is obsolete but not deprecated in the sense of pending removal.  Even [Intel's proposed x86S (which removes 16-bit mode)](https://www.intel.com/content/www/us/en/developer/articles/technical/envisioning-future-simplified-architecture.html) doesn't propose removing it from the hardware  
> **username_4** (2023-07-17): Rumors of Windows x64 not supporting MMX or x87 floating point are greatly exaggerated.  MSVC [removed MMX intrinsics](https://stackoverflow.com/questions/60933486/mmx-intrinsics-like-mm-cvtpd-pi32-not-found-with-msvc-2019-for-64bit-targets-c), but the kernel's context-switch code still saves/restores x87 state so machine code made with other toolchains continues to work fine.  
> **username_4** (2023-07-17): Despite what https://learn.microsoft.com/en-us/windows/win32/dxtecharts/sixty-four-bit-programming-for-game-developers#porting-applications-to-64-bit-platforms about future systems possibly not supporting MMX or x87, I find it implausible that future Windows would set control-register bits so x87 and MMX instructions faulted instead of working in 64-bit user-space.  Intel and AMD are very unlikely to remove them in the forseeable future, although they may continue to get a bit slower as dedicated HW support for them is removed to simplify other parts of the CPU.  
> **username_4** (2023-07-17): Some major software like FFmpeg's software decoder for h.264 (AVC) video uses hand-written MMX instructions, including in 64-bit mode, so it would break backwards compat with some commercially-relevant software; something vendors go to great lengths to avoid.  Perhaps relevant, the SSE3 feature bit (CPUID) indicates support for [x87 `fisttp`](https://www.felixcloutier.com/x86/fisttp) as well as a bunch of XMM instructions, although a hypothetical x86 without x87 could just not support that either.  
> **username_4** (2023-07-17): *the x87 FPU had no ability to store the 80 bits into memory.* - Not true: `fstp m80` and `fld m80` are valid (but significantly slower than m32/m64). https://www.felixcloutier.com/x86/fst:fstp . `fst m80` isn't valid, so you'd need to duplicate it first with `fld st0` if you want a copy left in the x87 stack after storing.  And you can't use an 80-bit long double as a memory source operand for any math instruction, e.g. `fadd m32/m64` are valid but not `fadd m80`.  See Nate's answer here and mine on [Did any compiler fully use x87 80-bit FP?](//retrocomputing.stackexchange.com/q/9751)  
> **username_8** (2023-07-17): x87 isn't deprecated by Intel, but it is highly discouraged, and Intel has explicitly stated that it has performance issues that they will not fix. User/Source Coding Rule 9 from the *Intel 64 and IA-32 Architectures Optimization Reference Manual* states: "Enable the compiler’s use of Intel SSE, Intel SSE2, Intel AVX, Intel AVX2, and possibly more advanced SIMD instruction sets (Intel AVX-512) with appropriate switches. Favor scalar SIMD code generation to replace x87 code generation."  
---
### Answer by username_9 on 2023-07-16. Score: 3
On many embedded platforms without floating-point units, computations using a 32-bit or 64-bit mantissa without an "implied 1" would be faster, more precise, and in just about every way better than those IEEE-754 64-bit double-precision values. Unfortunately, the way the C Standard added `long double` broke a key aspect of the language: that all floating-point values passed to variadic functions be converted to a common type.

If you're designing a language for use with embedded systems that may lack floating-point unit, I'd suggest having the following floating-point types:

* `float`: typically 32 bits total, unless configured to be a different size.
* `long float`: type to which `float` values are promoted for computations (and might be as small as `float` or as big as `long double`.
* `double`: typically 64 bits total, unless configured to be a different size.
* `long double`: type to which `double` values are promoted for computations.

I would then suggest having a means of explicitly passing types other than `double` to functions, but say that expressions that don't explicitly force the type of a floating-point value passed to a variadic function would by default be converted to `double`.

On many platforms, operations on 64-bit floating-point values would require unpacking the mantissa to a 64-bit combination of registers, performing computations with a 64-bit mantissa, and then packing the result. Processing an expression like `a+b+c` by unpacking `a` and `b`, adding them, normalizing and packing the result, and then unpacking that result along with `c`, adding them, and finally normalizing and packing that result, would take more work and yield less precise results than omitting the operations that would normalize and pack and then unpack the result from the original computation.

Extended floating-point types are sufficiently uncommon that most programmers wouldn't particularly favor them for their improved semantics, but if a set of development tools for an embedded system can perform floating-point calculations which are both faster and more precise than its competitors, that would be a nice advantage.
> **Comments:**
> **username_10** (2023-07-16): I would hope that a new language, if it is going to have variadic functions, would support them in a better way than the afterthought hack that is `<stdarg.h>` (and its predecessor `<varargs.h>`).  So hopefully the type conversion issue wouldn't arise at all.  
> **username_9** (2023-07-16): @NateEldredge: Even if a language has better calling convention for its own variadic functions (I could offer some ideas, if you'd want to hear them), it would still be useful to support interoperability with other languages that use the C calling convention for such functions.  
> **username_0** (2023-07-16): @supercat Swift supports calling C variadic functions via [`withVaList`](https://developer.apple.com/documentation/swift/withvalist(_:_:)). I’ll note that it only supports calling e.g. `vprintf` and not `printf` in this manner.  
> **username_9** (2023-07-16): @NateEldredge: I wish that compound literals had been specified as yielding static const lvalues when all values were compile-time constants, and non-l-values otherwise, but there was a convenient syntax to take a non-l-value, copy it to a temporary, and yield the address thereof, in sitautions where the resulting pointer is used in a dereferencing or function call expression.  
---
