# How can a 80-bit floating point be used by a 32-bit system? [duplicate]

Since a 32bit system can't manage a 2^33 number (because the obvious 32-bit limit), how can manage a 80-bit floating point number?

It should require "80-bit"...

• The same way it does a 64-bit floating point number. It uses 3 or (2) 32-bit registers. Since 80-bit floating point number isn't even a standard size, it would actually be a 96-bit number with only 80-bits being used. – Ramhound Jul 30 '14 at 15:24
• when you worry about platform bittedness, you are worried about the inner workings of the CPU as others have said, and the way instructions are natively run on the system. IEEE754 numbers are usually directly processable in the FPUs execution units of your CPU, whereas a 128bit number would require the use of multiple instructions programmed such that they can aggregate the meaning of the value the application is evaluating. that leaves the processing of the number up to the application. – Frank Thomas Jul 30 '14 at 15:31
• @Ƭᴇcʜιᴇ007 Not quite a dupe of that one. That one is more about the difference between a number and its text/ASCII representation, though some of those answers may also address this. – Bob Jul 30 '14 at 18:11
• In virtually all modern machines, floating point is handled by a separate processor (though usually on the same chip as the main processor). All such processors know how to handle 80-bit (though some do it a lot faster than others). (And the "width" of a processor is something of a fiction anyway.) – Daniel R Hicks Jul 31 '14 at 1:30
• @Ramhound: No, 80 bits is a 8087 peculiarity, and it uses 1 FP register. It is definitely not a 96 bits number. – MSalters Jul 31 '14 at 8:02

One of the meanings of a 32 bit CPU is that its registers are 32 bits wide. This doesn't mean it can't deal with say, 64 bit numbers, just that it has to deal with the lower 32 bit half first, then the upper 32 bit half second. (It's why CPUs have a carry flag.) It's slower than if the CPU could just load the values in a wider 64 bit register, but still possible.

Thus, the "bitness" of a system does not necessarily limit the size of the numbers a program can deal with, because you can always break up operations that wont fit into CPU registers into multiple operations. So it makes operations slower, consume more memory (if you have to use memory as a "scratchpad"), and more difficult to program, but the operations are still possible.

However, none of that matters with, for example, Intel 32 bit processors and floating point, as the floating point part of the CPU has its own registers and they are 80 bits wide. (Early in the x86's history, the floating point capability was a separate chip, it was integrated in the CPU beginning with 80486DX.)

Floating point values, insofar as they are stored in the FPU registers, work very different than binary integer values.

The 80 bits of a floating point value are divided amongst a mantissa and exponent (there is also the "base" in floating point numbers which is always 2). The mantissa contains the significant digits, and the exponent determines how large those significant digits are. So there is no "overflow" into another register, if your number gets too big to fit in the mantissa, your exponent increases and you lose precision - i.e. when you convert it to an integer, you'll lose decimal places off the right - this is why it's called floating point.

If your exponent is too large, you then have a floating-point overflow, but you can't easily extend it to another register since the exponent and mantissa are tied together.

I could be inaccurate and wrong about some of that, but I believe that's the gist of it. (This Wikipedia article illustrates the above a bit more succinctly.)

It's OK that this works totally differently since the whole "floating-point" part of the CPU is sort of in its own world - you use special CPU instructions to access it and such. Also, towards the point of the question, because it's separate, the bitness of the FPU isn't tightly coupled with bitness of the native CPU.

• Everything you added from my inspiration is correct, so no worries there :) Just one point I should mention, although you can use native CPU instructions where a floating-point unit exists, one can also perform floating point operations in software (with equivalent bitwise or integer math operations). To extend to that point, given enough memory, you can also have arbitrary precision numbers (as opposed to our fixed 64-bit, or 80-bit in this case) using software algorithms/libraries (one commonly used one is the GNU Multiple Precision library). – Breakthrough Jul 30 '14 at 18:03
• Nitpick: first Intel integrated FPU was in the 80486DX, not the 80386. – spudone Jul 30 '14 at 21:52
• @markzzz: Nothing is crazy if it is required. Using 16bit floats to simulate an atom bomb in order to evaluate your nuclear stockpile is crazy because it's not precise enough for you to be confident in the result. In which case 32bits is required (and yes, in the old days these calculations were done on 16 bit PDPs). Similarly using 32bit folats to simulate the climate is not precise enough due to the chaotic nature of the calculations. – slebetman Jul 31 '14 at 8:37
• FWIW, the common way to implement floating point operations on machines without FP units of the size you want is to do it in software using integer instructions. Because, at the end of the day both the exponent and mantissa of the floating point number are merely integers. It's how we interpret them that gives them their special meanings. – slebetman Jul 31 '14 at 8:39
• @PTwr on x86 you actually have 7 usable GPRs for data, EBP included. It's just that ABI of most languages implementations reserves this register for stack frame pointer. But e.g. with GCC you can use `-fomit-frame-pointer` to get that register back. – Ruslan Feb 29 '16 at 6:49

32-bit, 64-bit, and 128-bit all refer to the word length of the processor, which can be thought of as the "fundamental data type". Often, this is the number of bits transferred to/from the RAM of the system, and the width of pointers (although nothing stops you from using software to access more RAM then what a single pointer can access).

Assuming a constant clock speed (as well as everything else in the architecture being constant), and assuming memory reads/writes are the same speed (we assume 1 clock cycle here, but this is far from the case in real life), you can add two 64-bit numbers in a single clock cycle on a 64-bit machine (three if you count fetching the numbers from RAM):

``````ADDA [NUM1], [NUM2]
STAA [RESULT]
``````

We can also do the same computation on a 32-bit machine... However, on a 32-bit machine, we need to do this in software, since the lower 32-bits must be added first, compensate for overflow, then add the upper 64-bits:

``````     ADDA [NUM1_LOWER], [NUM2_LOWER]
STAA [RESULT_LOWER]
CLRA          ; I'm assuming the condition flags are not modified by this.
BRNO CMPS     ; Branch to CMPS if there was no overflow.
ADDA #1       ; If there was overflow, compensate the value of A.
STAA [RESULT_UPPER]
``````

Going through my made-up assembly syntax, you can easily see how higher-precision operations can take an exponentially longer time on a lower word length machine. This is the real key to 64-bit and 128-bit processors: they allow us to handle larger numbers of bits in a single operation. Some machines include instructions for adding other quantities with a carry (e.g. `ADC` on x86), but the example above has arbitrary precision values in mind.

Now, to extend this to the question, it's simple to see how we could add numbers larger than the registers we have available - we just break the problem up into chunks the size of the registers, and work from there. Although as mentioned by @MatteoItalia, the x87 FPU stack has native support for 80-bit quantities, in systems lacking this support (or processors lacking a floating point unit entirely!), the equivalent computations/operations must be performed in software.

So for an 80-bit number, after adding each 32-bit segment, one would also check for overflow into the 81-st bit, and optionally zero the higher order bits out. These checks/zeros are performed automatically for certain x86 and x86-64 instructions, where the source and destination operand sizes are specified (although these are only specified in powers of 2 starting from 1 byte wide).

Of course, with floating point numbers, one can't simply perform the binary addition since the mantissa and significant digits are packed together in offset form. In the ALU on an x86 processor, there is a hardware circuit to perform this for IEEE 32-bit and 64-bit floats; however, even in the absence of a floating-point unit (FPU), the same computations can be performed in software (e.g. through the use of the GNU Scientific Library, which uses an FPU when compiled on architectures with, falling back to software algorithms if no floating-point hardware is available [e.g. for embedded microcontrollers lacking FPUs]).

Given enough memory, one can also perform computations on numbers of arbitrary (or "infinite" - within realistic bounds) precision, using more memory as more precision is required. One implementation of this exists in the GNU Multiple Precision library, allowing unlimited precision (until your RAM is full, of course) on integer, rational, and floating point operations.

• You are failing to mention the most important detail: the x87 FPU on the x86 platform has 80-bit wide floating point registers, so its native calculations are actually done on 80 bits floats, no need to emulate anything in software. – Matteo Italia Jul 31 '14 at 7:40
• @MatteoItalia I see that now, thank you. I thought the original question was asking for a more generic overview of how one could perform operations on numbers larger than the processor word size, and not the specific implementation of extended 80-bit floats in x86 (also why my example was 90 bits instead of 80...). I've updated the answer now to better reflect this, thank you for the heads up. – Breakthrough Jul 31 '14 at 14:21

The memory architecture of the system may only allow you to move 32 bits at once - but that doesn't stop it from using larger numbers.

Think of multiplication. You may know your multiplication tables up to 10x10, yet you probably have no problem performing 123x321 on a piece of paper: you just break it into many small problems, multiplying individual digits, and taking care of the carry etc.

Processors can do the same thing. In the "olden days" you had 8 bit processors that could do floating point math. But they were slooooooow.

• They were slow only after a certain point. You could write "fast" floating point operations if you limited yourself to a certain specification. – Ramhound Jul 30 '14 at 15:30

"32-bit" is really a way of categorizing processors, not a set-in-stone ruling. a "32-bit" processor typically has 32 bit general purpose registers to work with.

However, there are no set in stone requirement that everything in the processor be done in 32-bit. For example, it was not unheard of for a "32-bit" computer to have a 28-bit address bus, because it was cheaper to make the hardware. 64-bit computers often only have a 40-bit or 48-bit memory bus for the same reason.

Floating point arithmetic is another place where sizes vary. Many 32-bit processors supported 64-bit floating point numbers. They did so by storing the floating point values in special registers that were wider than the general purpose registers. To store one of these large floating point numbers in the special registers, one would first split the number across two general purpose registers, then issue an instruction to combine them into a float in the special registers. Once in those floating point registers, the values would be manipulated as 64-bit floats, rather than as a pair of 32-bit halves.

The 80-bit arithmetic you mention is a special case of this. If you have worked with floating point numbers, you are familiar with the imprecision that arises from floating point round off issues. One solution for roundoff is to have more bits of precision, but then you have to store bigger numbers, and force developers to use unusually large floating point values in memory.

The Intel solution is that the floating point registers are all 80 bits, but the instructions to move values to/from those registers primarially work with 64-bit numbers. As long as you operate entirely within Intel's x87 floating point stack, all of your operations are done with 80 bits of precision. If your code needs to pull one of those values out of the floating point registers and store it somewhere, it truncates it to 64-bits.

Moral of the story: categorizations like "32-bit" are always hazier when you get deeper into things!

• But if I use 32bit floating point values into a 16bit system (or 64 bit floating point values into a 32 bit system) it requires only more memory (since it must twice registers)? Or processing the information will add overhead, so will take more time? – markzzz Jul 31 '14 at 6:51
• @markzzz: Working with multiple registers almost always takes more time – Mooing Duck Jul 31 '14 at 16:39
• Loading the 32bit floating point value into the special purpose registers will take more time. However, once they are stored as 32-bit floats in the special purpose floating point registers, the hardware will operate on those floating point values at full speed. Remember, "16-bit" only refers to the size of the GENERAL purpose registers. The floating point registers are specially sized for their task, and may be wider (32 bits wide in your case) – Cort Ammon Jul 31 '14 at 21:49

A "32-bit" CPU is one where most of the data registers are 32-bit registers, and most of the instructions operate on data in those 32-bit registers. A 32-bit CPU is also likely to transfer data to and from memory 32-bits at a time. Most of the registers being 32-bit does not mean all of the registers are 32-bit. The short answer is that a 32-bit CPU can have some features that use other bitcounts, such as 80-bit floating point registers and corresponding instructions.

As @spudone said in a comment on @ultrasawblade's answer, the first x86 CPU to have integrated floating-point operations was the Intel i486 (specifically the 80486DX but not the 80486SX), which, according to Page 15-1 of the i486 Microprocessor Programmers Reference Manual, includes in its numerical registers "Eight individually-addressable 80-bit numeric registers". The i486 has a 32-bit memory bus, so transferring an 80-bit value would take 3 memory operations.

The predecessor to the 486 generation, the i386, did not have any integrated floating-point operations. Instead, it had support for using an external floating point "coprocessor", the 80387. This coprocessor had nearly the same functionality that was integrated into the i486, as you can see from Page 2-1 of the 80387 Programmer's Reference Manual.

The 80-bit floating point format seems to have originated with the 8087, the math coprocessor for the 8086 and 8088. The 8086 and 8088 were 16-bit CPUs (with 16-bit and 8-bit memory buses), and were still able to use 80-bit floating point format, by taking advantage of the 80-bit registers in the coprocessor.