Why is IEEE-754 Floating Point not exchangable between platforms? - c++

It has been asserted that (even accounting for byte endian-ness) IEEE754 floating point is not guaranteed to be exchangeable between platforms.
So:
Why, theoretically, is IEEE floating point not exchangeable between platforms?
Are any of these concerns valid for modern hardware platforms (e.g. i686, x64, arm)?
If the concerns are valid, can you please demonstrate an example where this is the case (C or C++ is preferred)?
Motivation: Several GPS manufacturers exchange their binary formats for (e.g.) latitude, longitude and raw data in "IEEE-754 compliant floating point values". So, I don't have control to choose a text format or other "portable" format. Hence, my question has to when the differences may or may not occur.

IEEE 754 clause 3.4 specifies binary interchange format encodings. Given a floating-point format (below), the interchange format puts the sign bit in the most significant bit, biased exponent bits in the next most significant bits, and the significand encoding in the least significant bits. A mapping from bits to bytes is not specified, so a system could use little-endian, big-endian, or other ordering.
Clause 3.6 specifies format parameters for various format widths, including 64-bit binary, for which there is one sign bit, 11 exponent field bits, and 52 significand field bits. This clause also specifies the exponent bias.
Clauses 3.3 and 3.4 specify the data represented by this format.
So, to interchange IEEE-754 floating-point data, it seems systems need only to agree on two things: which format to use (e.g., 64-bit binary) and how to get the bits back and forth (e.g., how to map bits to bytes for writing to a file or a network message).

Related

What is stored in bits of long double variable? [duplicate]

What determines the representation of floating point numbers in the memory? By the compiler or FPU.
If the representation depends on the FPU, how the compiler stores constants such as 1.337f in a binary file? Maybe when the application starts happens unpacking of the floating point values?
I have long been interested in this question because do network programming.
The C and C++ standards do not require any particular floating point representations, although recent standards have included some specific support for IEEE (i.e. facilities that are available IF the implementation uses IEEE floating point). For particular implementations (aka toolchains) the representation of floating point depends on the host system and, to some extent, on decisions by compiler vendors.
For older microprocessor (and other processing hardware such as microcontrollers, Digital Signal Processors [DSP], etc), the implementation is often in hardware - for example, a set of specialised electronic circuits that implement registers that represent floating point values, and circuits which perform operations on such registers.
In modern processing hardware (microcontrollers,DSPs, graphic processing units, floating point units, etc) the implementation is in microcode - over-simplistically, a layer of hardware instructions that implement machine code instructions and a state machine (a basis for how the processor appears to work, as far as programs and operating systems are concerned). So higher level instruction sets (X86, etc) are used by executables and operating systems, and microcode is the intermediary between the operating system and the hardware (which often implements a very simple set of instructions). The term "modern" in this description is relative - the first microcode-based processors date from the 1970s.
Historically, processing hardware has implemented floating point in a wide number of ways - some proprietary, and some standardised. There are a number of processors which supported multiple distinct representations. In some cases, software layers have emulated floating point on top of hardware that does not support floating point at all. Most compilers will use hardware-supplied floating point if available (and some compilers have options to select different floating point representations, reflecting their target platforms), but a number of compilers targeting hardware with no floating point support literally emulate the representations and operations in software.
The IEEE floating point specification (first version related in 1985, most recent version IEEE 754-2008) which has been adopted as an international standard ISO/IEC/IEEE 60559:2011 defines a bunch of things, including arithmetic formats (how values, infinities, NaNs, etc are represented in floating point variables), interchange formats (encodings for exchange of floating point values between systems), operations (for arithmetic, etc), rounding rules during operations, and exception handling (dealing with things like division by zero). The IEEE specification has evolved over some time, and is becoming increasingly common in modern hardware and software.
Who determines the representation is the FPU which adheres to a standard with which the compiler supports.
The current standard is the IEEE 754. It describes how floating-point computation and data should be represented (see this article for a detailed description).
The data is always represented by a fixed number of bits, such as 32-bit, 64-bit, 128-bit, 80-bit (a.k.a x86 extended precision). In memory they are all but bits. But then, each set of bits represents a component of the floating-point data, such as: the most significant bit (depending on the endianness) is the sign, another set of bits are the exponent and another the significant part.
Then, the compiler's support of the standard (the IEEE 754) generates code specific to that representation.
So, user2079303's answer is right: who determines the representation of your code is the compiler which targets the standard, however it wouldn't work if the standard was not in charge.
EDIT: Peter's answer is quite detailed and covers many other cases.
What determines the representation of floating point numbers in the memory
The compiler determines which representation it uses. But, if it targets an fpu, then it must use the representation used by the fpu.
how the compiler stores constants such as 1.337f in a binary file?
Typically, in the same binary representation as it uses in memory.

What does The IEEE-754 mean by "interchange formats"?

The standard says that they support the exchange of floating-point data between implementations. What is meant by 'implementations' here? And how that exchange may occur?
The standard, like many programming standards, specifies a set of rules. Anything that follows those rules is an implementation of the standard. Per the IEEE 754-2008 abstract, “An implementation of a floating-point system conforming to this standard may be realized entirely in software, entirely in hardware, or in any combination of software and hardware.”
For example, one could, in theory, design a computer processor that natively supports floating-point formats and that has one instruction for each IEEE 754 operation. Or you could write a C compiler that targets a processor which has some floating-point operations, like addition and multiplication, but uses software routines for square root and other operations. Or you could write software that implements all the IEEE 754 operations using purely C integer operations, without any reliance on any specific hardware. Each of these could be an IEEE 754 implementation.
The floating-point behaviors are specified largely in terms of the mathematical values represented. For arithmetic formats, the standard specifies what numbers are represented, what the results of performing arithmetic operations on them are, and so on. Interchange formats go further than this; for interchange formats, the standard specifies precisely what bit patterns represent which values.
Because of this, if one IEEE 754 implementation puts a value in a floating-point object and then transmits the bits to another implementation, possibly running on completely different hardware, and that destination implementation restores those bits to a floating-point object of the same interchange format, then the destination object will have the same value and behavior as the original source object. In other words, the interchange formats make data portable.
This transmission can occur over a network, by storage of the bits onto a disk that is then physically moved to another computer, by printing the bits (or a representation of them, such as hexadecimal) on paper that is then typed in by a human, or other means.
The standard specifies only the bit pattern in a sequence of bits from most significant to least significant. It does not specify how those bits are grouped into bytes or how those bytes are ordered, so sending and receiving systems must be sure to send and receive the bits in the correct order.

What uncommon floating-point sizes exist in C++ compilers?

The C++14 draft standard seems rather quiet about the specific requirements for float, double and long double, although these sizes seem to be common:
float: IEEE 32-bit floating-point representation (roughly 7 digits of precision, exponent range of 1e-38..1e+38)
double: IEEE 64-bit floating-point representation (roughly 16 digits of precision, exponent range of 1e-308..1e+308)
long double: 80-bit floating-point representation (roughly 19 digits of precision, exponent range of 1e-4951..1e+4932)
What C++ compilers and systems currently use floating-point sizes other than these?
I'm interested in longer, shorter, and non-binary representations using the standard types, not libraries, as my primary interest is portability of C++ programs.
It's unclear what "uncommon sizes" you're talking about
If you're only asking about size in bits then "odd-sized" (i.e. not a power of 2) types usually exist in older platforms that don't use 8-bit (or another power of 2) bytes
One example is the Unisys ClearPath Dorado Servers with 36-bit float and 72-bit double. That beast is still even in active development until now. The last version was in 2018. Mainframes and servers live a very long life so you can still see some PDP-10 and other architectures in use in modern times, with modern compiler support.
But even in newer platforms you can still see some examples like Intel Itanium's 82-bit extended float format. Many platforms also use a 40-bit floating-point format. It's especially common in many modern DSPs that use 40-bit accumulators like the TI C3x/C4x, SHARC ADSP-21160, Atmel TSC21020F. There are also many old 40-bit floating-point formats like the IBM extended or Microsoft MBF extended formats. See also Why did 8-bit Basic use 40-bit floating point?
In addition there are some other non-standard 24-bit floats in a few modern C/C++ compilers for microcontrollers. And in computer graphics many minifloat formats like 10-bit or 11-bit floats aren't unknown, beside 16 and 24-bit floats
If you care about the formats then there are lots of standard compliant 32, 64 and 128-bit floating-point formats that aren't IEEE-754 like the hex and decimal floating point types in IBM z, Cray formats and VAX formats.
In fact IBM z is one of the very rare modern platforms with decimal float hardware, although if you use GCC and some other compilers you can use their built-in software support for decimal float. IBM also uses the special double-double format which is still the default for long double on PowerPC until now
Here's the summary of most of the available floating-point formats. See also Do any real-world CPUs not use IEEE 754?. For more information continue to the next section
Types in C++ are generally mapped to hardware types for performance reasons. Therefore floating-point types will be whatever available on the CPU if it ever has an FPU. In modern computers IEEE-754 is the dominant format in hardware, and due to the requirements in C++ standard float and double must be mapped to at least IEEE-754 single and double precision respectively
Hardware support for types with higher precision is not common except on x86 and a few other rare platforms with 80-bit extended precision, therefore long double is usually mapped to the same type as double on those platforms. However recently long double is being slowly migrated to IEEE-754 quadruple precision in many compilers like GCC or Clang. Since that one is implemented with the built-in software library, performance is a lot worse. Depending on whether you favor faster execution or higher precision you're still free to choose whatever type long double maps to though. For example on x86 GCC has -mlong-double-64/80/128 and -m96/128bit-long-double options to set the padding and format of long double. The option is also available in many other architectures like the S/390 and zSeries
PowerPC OTOH by default uses a completely different 128-bit long double format implemented using double-double arithmetic and has the same range as IEEE-754 double precision. Its precision is slightly lower than quadruple precision but it's a lot faster because it can utilize the hardware double arithmetic. As above, you can choose between the 2 formats with the -mabi=ibmlongdouble/ieeelongdouble options. That trick is also used in some platforms where only 32-bit float is supported to get near-double precision
IBM z mainframes traditionally use IBM hex float formats and they still use it nowadays. But they do also support IEEE-754 binary and decimal floating-point types in addition to that
The format of floating-point numbers can be either base 16 S/390® hexadecimal format, base 2 IEEE-754 binary format, or base 10 IEEE-754 decimal format. The formats are based on three operand lengths for hexadecimal and binary: short (32 bits), long (64 bits), and extended (128 bits). The formats are also based on three operand lengths for decimal: _Decimal32 (32 bits), _Decimal64 (64 bits), and _Decimal128 (128 bits).
Floating-point numbers
Other architectures may have other floating-point formats, like VAX or Cray. However since those mainframes are still being used, their newer hardware version also include support for IEEE-754 just like how IBM did with their mainframes
On modern platforms without FPU the floating-point types are usually IEEE-754 single and double precision for better interoperability and library support. However on 8-bit microcontrollers even single precision is too costly, therefore some compilers support a non-standard mode where float is a 24-bit type. For example the XC8 compiler uses a 24-bit floating-point format that is a truncated form of the 32-bit format, and NXP's MRK uses a different 24-bit float format
Due to the rise of graphics and AI applications that require a narrower floating-point type, 16-bit float formats like IEEE-754 binary16 and Google's bfloat16 are also introduced to in many platforms and compilers also have some limited support for them, like __fp16 in GCC
First of, I am new to Stack Overflow, so please bear with me.
However, to answer your question. Looking at the float.h headers, which specify floating point parameters for the:
Intel Compiler
//Float:
#define FLT_MAX 3.40282347e+38F
//Double:
#define DBL_MAX 1.7976931348623157e+308
//Long Double:
#if (__IMFLONGDOUBLE == 64) || defined(__LONGDOUBLE_AS_DOUBLE)
#define LDBL_MAX 1.7976931348623157e+308L
#else
#define LDBL_MAX 1.1897314953572317650213E+4932L
GCC (MinGW actually gcc 4 or 5)
//Float:
#define FLT_MAX 3.40282347e+38F
//Double:
#define DBL_MAX 1.7976931348623157e+308
//Long Double: (same as double for gcc):
#define LDBL_MAX 1.7976931348623157e+308L
Microsoft
//Float:
#define FLT_MAX 3.40282347e+38F
//Double:
#define DBL_MAX 1.7976931348623157e+308
//Long Double: (same as double for Microsoft):
#define LDBL_MAX DBL_MAX
So, as you can see only the Intel compiler provides 80-bit representation for long double on a "standard" Windows machine.
This data is copied from the respective float.h headers from a Windows machine.
float and double are de-facto standardised on the IEEE single and double precision representations. I would put assuming these sizes in the same category as assuming CHAR_BIT==8. Some older ARM systems did have weird "mixed-endian" doubles, but unless you are working with retro stuff you are unlikely to encounter that nowadays.
long double on the other hand is far more variable. Sometimes it's IEEE double precision, sometimes it's 80-bit x87 extended, sometimes it's IEEE quad precision , sometimes it's a "double double" format made up from two IEEE double precision numbers added together.
So in portable code you can't rely on long double being any better than double.

Floating point representation

What determines the representation of floating point numbers in the memory? By the compiler or FPU.
If the representation depends on the FPU, how the compiler stores constants such as 1.337f in a binary file? Maybe when the application starts happens unpacking of the floating point values?
I have long been interested in this question because do network programming.
The C and C++ standards do not require any particular floating point representations, although recent standards have included some specific support for IEEE (i.e. facilities that are available IF the implementation uses IEEE floating point). For particular implementations (aka toolchains) the representation of floating point depends on the host system and, to some extent, on decisions by compiler vendors.
For older microprocessor (and other processing hardware such as microcontrollers, Digital Signal Processors [DSP], etc), the implementation is often in hardware - for example, a set of specialised electronic circuits that implement registers that represent floating point values, and circuits which perform operations on such registers.
In modern processing hardware (microcontrollers,DSPs, graphic processing units, floating point units, etc) the implementation is in microcode - over-simplistically, a layer of hardware instructions that implement machine code instructions and a state machine (a basis for how the processor appears to work, as far as programs and operating systems are concerned). So higher level instruction sets (X86, etc) are used by executables and operating systems, and microcode is the intermediary between the operating system and the hardware (which often implements a very simple set of instructions). The term "modern" in this description is relative - the first microcode-based processors date from the 1970s.
Historically, processing hardware has implemented floating point in a wide number of ways - some proprietary, and some standardised. There are a number of processors which supported multiple distinct representations. In some cases, software layers have emulated floating point on top of hardware that does not support floating point at all. Most compilers will use hardware-supplied floating point if available (and some compilers have options to select different floating point representations, reflecting their target platforms), but a number of compilers targeting hardware with no floating point support literally emulate the representations and operations in software.
The IEEE floating point specification (first version related in 1985, most recent version IEEE 754-2008) which has been adopted as an international standard ISO/IEC/IEEE 60559:2011 defines a bunch of things, including arithmetic formats (how values, infinities, NaNs, etc are represented in floating point variables), interchange formats (encodings for exchange of floating point values between systems), operations (for arithmetic, etc), rounding rules during operations, and exception handling (dealing with things like division by zero). The IEEE specification has evolved over some time, and is becoming increasingly common in modern hardware and software.
Who determines the representation is the FPU which adheres to a standard with which the compiler supports.
The current standard is the IEEE 754. It describes how floating-point computation and data should be represented (see this article for a detailed description).
The data is always represented by a fixed number of bits, such as 32-bit, 64-bit, 128-bit, 80-bit (a.k.a x86 extended precision). In memory they are all but bits. But then, each set of bits represents a component of the floating-point data, such as: the most significant bit (depending on the endianness) is the sign, another set of bits are the exponent and another the significant part.
Then, the compiler's support of the standard (the IEEE 754) generates code specific to that representation.
So, user2079303's answer is right: who determines the representation of your code is the compiler which targets the standard, however it wouldn't work if the standard was not in charge.
EDIT: Peter's answer is quite detailed and covers many other cases.
What determines the representation of floating point numbers in the memory
The compiler determines which representation it uses. But, if it targets an fpu, then it must use the representation used by the fpu.
how the compiler stores constants such as 1.337f in a binary file?
Typically, in the same binary representation as it uses in memory.

What is the binary format of a floating point number used by C++ on Intel based systems?

I am interested to learn about the binary format for a single or a double type used by C++ on Intel based systems.
I have avoided the use of floating point numbers in cases where the data needs to potentially be read or written by another system (i.e. files or networking). I do realise that I could use fixed point numbers instead, and that fixed point is more accurate, but I am interested to learn about the floating point format.
Wikipedia has a reasonable summary - see http://en.wikipedia.org/wiki/IEEE_754.
Burt if you want to transfer numbers betwen systems you should avoid doing it in binary format. Either use middleware like CORBA (only joking, folks), Tibco etc. or fall back on that old favourite, textual representation.
This should get you started : http://docs.sun.com/source/806-3568/ncg_goldberg.html. (:
Floating-point format is determined by the processor, not the language or compiler. These days almost all processors (including all Intel desktop machines) either have no floating-point unit or have one that complies with IEEE 754. You get two or three different sizes (Intel with SSE offers 32, 64, and 80 bits) and each one has a sign bit, an exponent, and a significand. The number represented is usually given by this formula:
sign * (2**(E-k)) * (1 + S / (2**k'))
where k' is the number of bits in the significand and k is a constant around the middle range of exponents. There are special representations for zero (plus and minus zero) as well as infinities and other "not a number" (NaN) values.
There are definite quirks; for example, the fraction 1/10 cannot be represented exactly as a binary IEEE standard floating-point number. For this reason the IEEE standard also provides for a decimal representation, but this is used primarily by handheld calculators and not by general-purpose computers.
Recommended reading: David Golberg's What Every Computer Scientist Should Know About Floating-Point Arithmetic
As other posters have noted, there is plenty of information about on the IEEE format used by every modern processor, but that is not where your problems will arise.
You can rely on any modern system using IEEE format, but you will need to watch for byte ordering. Look up "endianness" on Wikipedia (or somewhere else). Intel systems are little-endian, a lot of RISC processors are big-endian. Swapping between the two is trivial, but you need to know what type you have.
Traditionally, people use big-endian formats for transmission. Sometimes people include a header indicating the byte order they are using.
If you want absolute portability, the simplest thing is to use a text representation. However that can get pretty verbose for floating point numbers if you want to capture the full precision. 0.1234567890123456e+123.
Intel's representation is IEEE 754 compliant.
You can find the details at http://download.intel.com/technology/itj/q41999/pdf/ia64fpbf.pdf .
Note that decimal floating-point constants may convert to different floating-point binary values on different systems (even with different compilers on the same system). The difference would be slight -- maybe only as large as 2^-54 for a double -- but is a difference nonetheless.
Use hexadecimal constants if you want to guarantee the same floating-point binary value on any platform.