3

I decided to go through Microsoft's C++ course on edX. According to them, double and long double are identical in size and range:

Microsoft C++ on edX

This contradicts these specs: https://en.wikipedia.org/wiki/C_data_types

At first, I thought Microsoft make a typo, but then I found this post:

On the x86 architecture, most compilers implement long double as the 80-bit extended precision type supported by that hardware (sometimes stored as 12 or 16 bytes to maintain data structure

and

Compilers may also use long double for a 128-bit quadruple precision format, which is currently implemented in software.

In other words, yes, a long double may be able to store a larger range of values than a double. But it's completely up to the compiler.

So, I thought to myself that, in most cases (i.e., in most compiler implementations), Microsoft is wrong, but in some cases they might be right.

But then I reviewed C++ 11 standard. And here what it says:

There are three floating point types: float, double, and long double. The type double provides at least as much precision as float, and the type long double provides at least as much precision as double. The set of values of the type float is a subset of the set of values of the type double; the set of values of the type double is a subset of the set of values of the type long double.

Vague, right?

So, here's the question: C++ has been around for a long time. Why is there still no solid common standard for this kind of stuff? Is it intentional for the sake of flexibility -- everyone decides for themselves what these data types will be. Or they just can't get everyone on board?

P.S. I still think Microsoft should've written it out differently: something along the lines of "long double is the same size as double or more (up to ... bytes)".

InfiniteLoop
  • 387
  • 1
  • 3
  • 18
  • This might not be the answer to your question, but the "C++ has been around for a long time" is kind of a double edged sword. Yes, they could define a standard, but this would break backward compatibility. This is no hard counter argument, as breaking backward compatibility happens, but I assume that this might be one reason. – Jerome Reinländer May 03 '18 at 07:59
  • To allow conforming programs to run on hardware that only has a single floating point data size. Think older hardware, embedded systems, micro controllers etc. – Richard Critten May 03 '18 at 08:18
  • The chip manufacturers could not resist the temptation to provide their processors with extended floating point formats. Considered a value-add in the olden days, but a unmitigated disaster to software developers who can't get their programs to produce consistent results. IEEE-754 is an industry standard and like all such standards is but a compromise that needed to make everyone equally unhappy. Microsoft wisely never jumped on that band-wagon, GCC did. What you get depends on what kind of processor you run. x86, power-pc and sparc have them, not the same. – Hans Passant May 03 '18 at 09:36
  • Maybe it'll be just as shocking to you to find out that all fundamental C types have no set size either! They are all defined in terms of `at least`: http://en.cppreference.com/w/cpp/language/types As for `long double` - very old VS versions (16 bit ones) used x87 80bit float type for long double, but with introduction of SSE it's became obsolete. gcc kept the 80 bit type, VS went with long double == double. – Dan M. May 03 '18 at 11:13
  • each implementation must specify the exact size of each type, otherwise how can people know how big is `long double` on that specific platform? Statements such as *"long double is the same size as double or more (up to ... bytes)"* is vague and is just for the standard – phuclv Sep 12 '19 at 00:14
  • 1
    Microsoft is talking about its own compiler. As always, they talk about their own products, not about standards. – user207421 Sep 12 '19 at 02:09

1 Answers1

1

According to them, double and long double are identical in size and range (see the screenshot below). This contradicts these specs:

There's no contradiction here. If you've read the standard and the linked article carefully you'll see that the C standard specifies the minimum possible ranges of types and not some fixed sizes for them. Also note that the wiki link above is for C and not C++. They're very different languages. Fortunately they both agree on types' sizes

That allows for flexibility because C and C++ have always been designed for portability. The main philosophy is "you don’t pay for what you don’t use" so compiler implementations must choose what's best and efficient for each specific architecture. They can't force int to be always 32-bit long. For example on an 18-bit architecture int will have 18 bits instead of an awkward 32-bit size, and 16-bit CPUs won't have to waste cycles supporting 32-bit int if int is defined to be a 16-bit type

That's why C and C++ allow for 1's complement and sign-magnitude for signed integer types instead of only 2's complement. Floating-point types also aren't mandated to have fixed sizes or be in binary so implementations can use decimal floating-point types. In fact IEEE-754 is not even required because there were computers that use other floating-point formats. See Do any real-world CPUs not use IEEE 754?

So reading the ISO/IEC 9899:201x C++ standard, section 5.2.4.2.1 (Sizes of integer types <limits.h>) we know that:

The values given below shall be replaced by constant expressions suitable for use in #if preprocessing directives. […] Their implementation-defined values shall be equal or greater in magnitude (absolute value) to those shown, with the same sign

The value corresponding to INT_MAX is 32767 which means that INT_MAX >= 32767 in any conforming implementations. The same thing applies to floating-point types in section 5.2.4.2.2 (Characteristics of floating types <float.h>)

What you read in the standard means

  • precisionOf(float) ⩽ precisionOf(double) ⩽ precisionOf(long double) and
  • setOfValues(float) ⊂ setOfValues(double) ⊂ setOfValues(long double)

It's very clear and not vague at all. In fact you're only guaranteed to have

  • CHAR_BIT ⩾ 8
  • sizeof(char) ⩽ sizeof(short) ⩽ sizeof(int) ⩽ sizeof(long) ⩽ sizeof(long long)
  • sizeof(float) ⩽ sizeof(double) ⩽ sizeof(long double)
  • FLT_DECIMAL_DIG ⩾ 6
  • LDBL_DECIMAL_DIG ⩾ DBL_DECIMAL_DIG ⩾ 10

So double is allowed to have the same size and precision as long double. In fact it's common for them to be the same type on non-x86 platforms because they don't have an extended precision type like x86. Recently many implementations have switched to a software-based IEEE-754 quadruple-precision for long double, but of course it'll be a lot slower

Because there are many possible formats for long double, there are also various options to choose the long double size, like when people don't need the extra precision or don't want the bad performance of a software implementation. For example

  • GCC for x86 has -mlong-double-64/80/128 depending on whether you want a 64-bit (i.e. same as double), 80-bit (extended precision) or 128-bit (quadruple-precision) long double. Similarly there are -m96/128bit-long-double options if you want to trade speed for 25% memory usage
  • GCC for PowerPC has -mabi=ibmlongdouble/ieeelongdouble for a double-double implementation or a standard quadruple-precision format which has a wider range and slightly better precision but is much slower

C++ has been around for a long time. Why is there still no solid common standard for this kind of stuff? Is it intentional for the sake of flexibility -- everyone decides for themselves what these data types will be

Exactly, as I said above, it's intentional for the sake of flexibility. You don't pay for the loss of performance and memory usage when you don't need the precision higher than double. The standard committee cares about every possible architectures out there, including some possibly dead ones1

In fact allowing higher long double sometimes lead to unexpected results when FLT_EVAL_METHOD = 2 because float and double operations are also done in higher precision, so the same expression at different points may have different results. Besides, it's impossible to vectorize the odd extended-precision long double math2 which results in even worse performance. Therefore even in x86 MS's cl.exe compiler completely disable the use of 80-bit long double

See also


1 See


2 See

phuclv
  • 37,963
  • 15
  • 156
  • 475
  • But having x87 precision set to 53 bits doesn't help with `FLT_EVAL_METHOD` for `float` if we don't use SSE, does it? – Ruslan Sep 11 '19 at 15:26
  • @Ruslan yes you're correct. That can happen if FLT_EVAL_METHOD > 0 – phuclv Sep 11 '19 at 16:42