Related
I'm optimizing a sorting function for a numerics/statistics library based on the assumption that, after filtering out any NaNs and doing a little bit twiddling, floats can be compared as 32-bit ints without changing the result and doubles can be compared as 64-bit ints.
This seems to speed up sorting these arrays by somewhere on the order of 40%, and my assumption holds as long as the bit-level representation of floating point numbers is IEEE 754. Are there any real-world CPUs that people actually use (excluding in embedded devices, which this library doesn't target) that use some other representation that might break this assumption?
https://en.wikipedia.org/wiki/Single-precision_floating-point_format
(binary32, aka float in systems that use IEEE754)
https://en.wikipedia.org/wiki/Double-precision_floating-point_format
(binary64, aka double in systems that use IEEE754)
Other than flawed Pentiums, any x86 or x64-based CPU is using IEEE 754 as their floating-point arithmetic standard.
Here are a brief overview of the FPA standards and their adoptions.
IEEE 754: Intel x86, and all RISC systems (IBM Power
and PowerPC, Compaq/DEC Alpha, HP PA-RISC,
Motorola 68xxx and 88xxx, SGI (MIPS) R-xxxx,
Sun SPARC, and others);
VAX: Compaq/DEC
IBM S/390: IBM (however, in 1998, IBM added an IEEE 754
option to S/390)
Cray: X-MP, Y-MP, C-90; other Cray models have been
based on Alpha and SPARC processors with
IEEE-754 arithmetic.
Unless your planning on supporting your library on fairly exotic CPU architectures, it is safe to assume that for now 99% of CPUs are IEEE 754 compliant.
It depends on where you draw the line between the "real world" and the imaginary one.
Vax G format is still supported on Alpha machines (which HP says they will support through at least 2013).
IBM hexadecimal FP is still supported by IBM z-series mainframes. They've added IEEE binary and decimal support, but from what I've heard they're rarely used, because the hexadecimal FP is quite a bit faster (IBM's been optimizing it for about 45 years now...)
Until fairly recently, Unisys still sold ClearPath IX servers that supported the Burroughs FP format, and ClearPath MCP machines that supported the Univac FP format. I believe those are now only run in emulation (on Xeons) but from a software viewpoint, they'll probably continue in active use for another decade or more.
There are even a few people still using DtCyber to run Plato on (emulated) Control Data mainframes, with their unique floating point format. (Sorry, but my first serious programming was on a CDC Cyber machine, so I couldn't resist bringing it up, even if it hasn't been "real world" for decades).
The Cell Processor's SPUs differ in a few ways (like lack of INF and NANs), but I don't think there are differences would break your assumptions...
PowerPC processors (Macs until about 2006-2007, tons of current IBM servers) use a 128 bit format consisting of two doubles for long double, instead if the IEEE 754 extended format.
However, in C or Objective-C, there is no portable way to interpret a 32 bit or 64 bit floating point number as an integer (assuming float and uint32_t, or double and uint64_t have the same number of bits). When I needed to do that kind of thing, I had to write different code depending on the compiler (one was using a union, one was by casting double* to long long*). No idea whether a reinterpretcast in C++ will do it portably.
Many real-world CPUs don't have any native floating-point format. Many implementations of C and other languages for such CPUs bundle libraries that use IEEE-754 single and double-precision formats and omit the extended-precision format despite the fact that other formats would be more suitable for many purposes.
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.
This question already has answers here:
How to check if C++ compiler uses IEEE 754 floating point standard
(2 answers)
Closed 7 years ago.
I have scientific data dumped into files. At the moment I've just dumped them with the same representation as they are in memory. I have documented that they are IEEE754 but I would love to have this asserted in the code so that if it gets ported to a weird architecture and separated from my documentation (research codes get passed around) it errors on compilation. At the moment I have
static_assert(sizeof(double)==8), "message");
Is there a way to test IEEE754? And can that be static asserted?
In C, check if this is defined:
__STDC_IEC_559__
In C++, check if this is true:
std::numeric_limits<double>::is_iec559()
IEC559, short for International Electrotechnical Commission standard 559,is the same as IEEE 754.
The answer for C++ is here: How to check if C++ compiler uses IEEE 754 floating point standard
For C, Annex F of the current C Standard specifies that the preprocessor constant __STDC_IEC_559__ will be pre-defined to the value 1 if the platform conforms to the IEEE 754 specification for floating point arithmetic. But older C compilers may not pre-define it even if floats are indeed IEEE 754.
Yet this is not enough for either language: this conformity only guarantees the semantics of IEEE 754, not the binary representation, and since you are dumping the binary representation to a file, you would also need to handle the endianness issue. It becomes even more complicated as endianness for integers may differ from the endianness for floats.
In the end, it is much better to use a textual representation to store your floating point values if you wish to achieve portability between various platforms, present and future. Of course, you will need to use the maximum precision for this representation.
Another solution is provided by http://hdfgroup.org that handle this very problem efficiently for large quantities of data.
You could use std::numeric_limits<double>'s is_iec559 - see here
According to the following site:
http://en.cppreference.com/w/cpp/language/types
"double - double precision floating point type. Usually IEEE-754 64 bit floating point type".
It says "usually". What other possible formats/standard could C++ double use? What compiler uses an alternative to the IEEE format? Or architecture?
Vaxen, Crays, and IBM mainframes, to name just a few that are still in reasonably wide use. Most (all?) of those can also do IEEE floating point now, but sometimes only with a special add-on. In other cases (IBM) IEEE arithmetic can carry a significant speed penalty.
As for older machines, most mainframes (Unisys, Control Data, etc.) used unique floating point formats, most of which weren't even much like IEEE, not to mention actually conforming.
For a short history lesson, you can check out the Intel Floating Point Case Study.
Intel compilers have an option that is on by default when optimizing that enables a so-called fast-math feature. This makes the math much faster but drops strict compliance with IEEE standards. One can enforce strict standard compliance with the fp-model option.
I believe the CUDA language for NVidia GPU's also has a significantly faster math library if one is willing to give up strict compliance with the IEEE standard. This not only makes the math faster, but it reduces the number of registers used for transcendental functions in particular.
Whether compliance is needed depends on a case-by-case basis. We've experienced problems with the Intel optimizations and have had to turn on the fp-model strict option to ensure correct results with double precision math.
Seems most computers today use IEEE-754. But alternatives seems to have been available before. Formats like excess 128 and packed BCD have been used before (http://aplawrence.com/Basics/floatingpoint.html). The wikipedia entry too has a few listed http://en.wikipedia.org/wiki/Floating_point
It is probably worth adding, in answer to "What other possible formats/standard could C++ double use?", that gcc for Atmel AVR (which are 8 bit data CPU's, used in some Arduinos) does not implement double as 64 bits.
See the GCC wiki, avr-gcc page and specifically the 'double' subsection of 'Deviations from the Standard' where it says
double is only 32 bits wide and implemented in the same way as
float
I believe other CPUs have similar implementations, but I couldn't find them.
I know that the C and C++ standards leave many aspects of the language implementation-defined just because if there was an architecture with other characteristics, a standard confirming compiler for that architecture would need to emulate those parts of the language, resulting in inefficient machine code.
Surely, 40 years ago every computer had its own unique specification. However, I don't know of any architectures used today where:
CHAR_BIT != 8
signed is not two's complement (I heard Java had problems with this one).
Floating point is not IEEE 754 compliant (Edit: I meant "not in IEEE 754 binary encoding").
The reason I'm asking is that I often explain to people that it's good that C++ doesn't mandate any other low-level aspects like fixed sized types†. It's good because unlike 'other languages' it makes your code portable when used correctly (Edit: because it can be ported to more architectures without requiring emulation of low-level aspects of the machine, like e.g. two's complement arithmetic on sign+magnitude architecture). But I feel bad that I cannot point to any specific architecture myself.
So the question is: what architectures exhibit the above properties?
† uint*_ts are optional.
Take a look at this one
Unisys ClearPath Dorado Servers
offering backward compatibility for people who have not yet migrated all their Univac software.
Key points:
36-bit words
CHAR_BIT == 9
one's complement
72-bit non-IEEE floating point
separate address space for code and data
word-addressed
no dedicated stack pointer
Don't know if they offer a C++ compiler though, but they could.
And now a link to a recent edition of their C manual has surfaced:
Unisys C Compiler Programming Reference Manual
Section 4.5 has a table of data types with 9, 18, 36, and 72 bits.
None of your assumptions hold for mainframes. For starters, I don't know
of a mainframe which uses IEEE 754: IBM uses base 16 floating point, and
both of the Unisys mainframes use base 8. The Unisys machines are a bit
special in many other respects: Bo has mentioned the 2200 architecture,
but the MPS architecture is even stranger: 48 bit tagged words.
(Whether the word is a pointer or not depends on a bit in the word.)
And the numeric representations are designed so that there is no real
distinction between floating point and integral arithmetic: the floating
point is base 8; it doesn't require normalization, and unlike every
other floating point I've seen, it puts the decimal to the right of the
mantissa, rather than the left, and uses signed magnitude for the
exponent (in addition to the mantissa). With the results that an
integral floating point value has (or can have) exactly the same bit
representation as a signed magnitude integer. And there are no floating
point arithmetic instructions: if the exponents of the two values are
both 0, the instruction does integral arithmetic, otherwise, it does
floating point arithmetic. (A continuation of the tagging philosophy in
the architecture.) Which means that while int may occupy 48 bits, 8
of them must be 0, or the value won't be treated as an integer.
Full IEEE 754 compliance is rare in floating-point implementations. And weakening the specification in that regard allows lots of optimizations.
For example the subnorm support differers between x87 and SSE.
Optimizations like fusing a multiplication and addition which were separate in the source code slightly change the results too, but is nice optimization on some architectures.
Or on x86 strict IEEE compliance might require certain flags being set or additional transfers between floating point registers and normal memory to force it to use the specified floating point type instead of its internal 80bit floats.
And some platforms have no hardware floats at all and thus need to emulate them in software. And some of the requirements of IEEE 754 might be expensive to implement in software. In particular the rounding rules might be a problem.
My conclusion is that you don't need exotic architectures in order to get into situations were you don't always want to guarantee strict IEEE compliance. For this reason were few programming languages guarantee strict IEEE compliance.
I found this link listing some systems where CHAR_BIT != 8. They include
some TI DSPs have CHAR_BIT == 16
BlueCore-5 chip (a Bluetooth
chip from Cambridge Silicon Radio) which has CHAR_BIT ==
16.
And of course there is a question on Stack Overflow: What platforms have something other than 8-bit char
As for non two's-complement systems there is an interesting read on
comp.lang.c++.moderated. Summarized: there are platforms having ones' complement or sign and magnitude representation.
I'm fairly sure that VAX systems are still in use. They don't support IEEE floating-point; they use their own formats. Alpha supports both VAX and IEEE floating-point formats.
Cray vector machines, like the T90, also have their own floating-point format, though newer Cray systems use IEEE. (The T90 I used was decommissioned some years ago; I don't know whether any are still in active use.)
The T90 also had/has some interesting representations for pointers and integers. A native address can only point to a 64-bit word. The C and C++ compilers had CHAR_BIT==8 (necessary because it ran Unicos, a flavor of Unix, and had to interoperate with other systems), but a native address could only point to a 64-bit word. All byte-level operations were synthesized by the compiler, and a void* or char* stored a byte offset in the high-order 3 bits of the word. And I think some integer types had padding bits.
IBM mainframes are another example.
On the other hand, these particular systems needn't necessarily preclude changes to the language standard. Cray didn't show any particular interest in upgrading its C compiler to C99; presumably the same thing applied to the C++ compiler. It might be reasonable to tighten the requirements for hosted implementations, such as requiring CHAR_BIT==8, IEEE format floating-point if not the full semantics, and 2's-complement without padding bits for signed integers. Old systems could continue to support earlier language standards (C90 didn't die when C99 came out), and the requirements could be looser for freestanding implementations (embedded systems) such as DSPs.
On the other other hand, there might be good reasons for future systems to do things that would be considered exotic today.
CHAR_BITS
According to gcc source code:
CHAR_BIT is 16 bits for 1750a, dsp16xx architectures.
CHAR_BIT is 24 bits for dsp56k architecture.
CHAR_BIT is 32 bits for c4x architecture.
You can easily find more by doing:
find $GCC_SOURCE_TREE -type f | xargs grep "#define CHAR_TYPE_SIZE"
or
find $GCC_SOURCE_TREE -type f | xargs grep "#define BITS_PER_UNIT"
if CHAR_TYPE_SIZE is appropriately defined.
IEEE 754 compliance
If target architecture doesn't support floating point instructions, gcc may generate software fallback witch is not the standard compliant by default. More than, special options (like -funsafe-math-optimizations witch also disables sign preserving for zeros) can be used.
IEEE 754 binary representation was uncommon on GPUs until recently, see GPU Floating-Point Paranoia.
EDIT: a question has been raised in the comments whether GPU floating point is relevant to the usual computer programming, unrelated to graphics. Hell, yes! Most high performance thing industrially computed today is done on GPUs; the list includes AI, data mining, neural networks, physical simulations, weather forecast, and much much more. One of the links in the comments shows why: an order of magnitude floating point advantage of GPUs.
Another thing I'd like to add, which is more relevant to the OP question: what did people do 10-15 years ago when GPU floating point was not IEEE and when there was no API such as today's OpenCL or CUDA to program GPUs? Believe it or not, early GPU computing pioneers managed to program GPUs without an API to do that! I met one of them in my company. Here's what he did: he encoded the data he needed to compute as an image with pixels representing the values he was working on, then used OpenGL to perform the operations he needed (such as "gaussian blur" to represent a convolution with a normal distribution, etc), and decoded the resulting image back into an array of results. And this still was faster than using CPU!
Things like that is what prompted NVidia to finally make their internal data binary compatible with IEEE and to introduce an API oriented on computation rather than image manipulation.