www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - We need to clarify if 'real' is the 'default floating point type'

reply Don Clugston <dac nospam.com.au> writes:
The spec is not quite clear on what 'real' can be and when it should be used.
For DMD, 'real' is the 80-bit (actually 79bit) X87 extended floating-point
type. 
This has the following characteristics:
a. it is the highest precision floating-point type supported by the hardware;
b. it is the highest precision floating-point type which is fast;
c. it is almost as fast as double;
d. it is the precision used internally for all floating point calculations (And 
this makes it the only precision which is anomoly-free).
e. it is an IEEE floating-point type.

In the spec, 'real' is defined as the "largest hardware implemented floating 
point size (Implementation Note: 80 bits for Intel CPUs)", so we can apparently 
rely on (a); but unfortunately, that's not enough to give us usage rules.
Here are some of the important scenarios:

If you are writing exclusively for X87, the usage rule is simple: use float or 
double for arrays,
(for cache efficiency), use real for everything else. Using 'real' instead of 
'double' almost never costs you anything, and it frequently provides benefits 
from the increased precision, and by avoiding anomolies caused by
characteristic 
(d).

On AMD64, it's possible to implement a D compiler which only uses SSE floating 
point, ignoring the X87 entirely. Then 'real' is 'double'. Points 'a'..'e' are 
all supported, albeit trivially. It makes no difference whether you use 'real' 
or 'double'.
But, a compiler which primarily uses SSE but also supports X87 is also
possible. 
Then 'real'=80 bits, but point 'd' does not apply -- double*double uses
'double' 
as the intermediate type.

Now consider the SPARC (late models only). This has 'real' = quadruple, 128 
bits. In theory it's
supported by hardware, but many operatons are emulated in software in response 
to 'unimplemented opcode' traps.
Consequently, it's slow. (10X slower than double?).
If writing for SPARC, the usage rule is: use 'real' when you truly need the 
extra precision. Otherwise, use double.

On the PowerPC, GDC currently implements 'real' as 'doubledouble' (128 bits) -- 
with a longer mantissa than 'real' on X87, but with an exponent range the same 
as 'double'. This isn't a true IEEE type (subnormal numbers don't work 
properly). Because PowerPC has a FMA instruction, this type is reasonably fast 
(~3X slower than double?) It satisfies (b) but not (c).

Q1. Is this 'doubledouble' acceptable as 'real'?

Q2. Is 'real' the 'default type to use, unless you are in a speed-critical
code',
OR is it 'the type to use when precision is useful and speed is irrelevant'
OR something else?

IMPLICATIONS: If 'real' is allowed to be relatively slow, then 'double' is the 
most important precision, and the math library functions need explicit
overloads 
for double precision (since 'double' is guaranteed to be fast on all platforms).

Nothing in the compiler needs to change, but we need some more guarantees in
the 
spec for what 'real' can be and how it is allowed to behave.
Mar 03 2008
next sibling parent =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Don Clugston wrote:

 The spec is not quite clear on what 'real' can be and when it should be 
 used.

That is quite an understatement! I think it reads something like: "`real` is x87 for both platforms that DMD cares about, and can map straightly onto C's long double type for you other weirdos" And then comes "irreal" and "cereal", and makes it surreal...
 Nothing in the compiler needs to change, but we need some more 
 guarantees in the spec for what 'real' can be and how it is allowed to 
 behave.

It would be nice if the spec covered other platforms and compilers, something I don't think is going to happen for D - maybe for D 2.0 ? Once upon a time I got annoyed by all of this (and bools and strings), but then gave up on it and just stopped using the "real" type in D. --anders
Mar 03 2008
prev sibling next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Don,

[good stuff removed]

this looks like a good cases for a pile of aliases (and the overloads you 
mentioned).

alias for:

fast_*,   largest with much speed penalty
best_*,  best supported 
HW_*,   w/ full HW support
IEEE_*, IEEE compliant
pow2_*, power of 2 length
...

these would be used like:  best_HW_IEEE_pow2_real (or something like that)
Mar 03 2008
parent reply Don Clugston <dac nospam.com.au> writes:
BCS wrote:
 Reply to Don,
 
 [good stuff removed]
 
 this looks like a good cases for a pile of aliases (and the overloads 
 you mentioned).
 
 alias for:
 
 fast_*,   largest with much speed penalty
 best_*,  best supported HW_*,   w/ full HW support
 IEEE_*, IEEE compliant
 pow2_*, power of 2 length
 ...
 
 these would be used like:  best_HW_IEEE_pow2_real (or something like that)

Well, IMHO, 'real' is a fine name for the largest without speed penalty. It's nice and short, and it sounds like a default type. The only other one you'd want is 'best_real' -- 'real' is viable for that, too, but names like longdouble and longreal come to mind. One workable option would be to split the current 'real' type into real80, doubledouble, and quadruple (depending on the system) and then we had: version(X86) { // more precisely, X86 using the X87 coprocessor typedef real80 real; } else { typedef double real; } version(X86) { typedef real80 longreal; } else version(PPC) { typedef doubledouble longreal; } else version(Sparc){ typedef quadruple longreal; } else { typedef double longreal; } The interesting thing about those types is that they can be emulated on any system. On some systems, multiple options can be implemented efficiently. For example, on Itanium, you have 80-bit reals AND a fma instruction. So you can do real80, doubledouble, the funky internal itanium 81-bit real, and could even do a real80real80 type.
Mar 03 2008
parent =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
Don Clugston wrote:

 One workable option would be to split the current 'real' type into 
 real80, doubledouble, and quadruple (depending on the system) and then 
 we had:

I think I used "extended" for real80, not that it really matters but. And "quadruple" is a bit long in the tooth, so "quad" rolls better... http://en.wikipedia.org/wiki/Extended_precision http://en.wikipedia.org/wiki/Quad_precision --anders
Mar 03 2008
prev sibling next sibling parent reply "Lionello Lunesu" <lionello lunesu.remove.com> writes:
D should establish some kind of politburo. A group of elders, each with his 
(..her?) own problem domain. Don should clearly govern the floating point 
aspects of D and just get to do what he thinks is best. : )

To me, this 'real' problem sounds a lot like the 'int' problem: why should 
'int' be established as the default integral type, when it is inefficient on 
64-bit hardware (and on 16-bit, 8-bit, .... )?

D's very proud of having fixed sized types, and rightly so, but there 
clearly are situations where size doesn't matter, and shouldn't matter. Of 
course, there's a minimum limit, when any smaller would just be ridiculous. 
...uh... nevermind.

So I think we should have 2 new aliases. One for a default int (at least 
32-bit) and one for a default float (at least double). On X86/87 these new 
aliases would map to int and real respectively.

Coming up with good names without silly adjectives/underscores will be 
difficult, though. I, for one, can't come up with any names that 'have it in 
them' to replace the omnipresent 'int' and 'real'...

L. 
Mar 03 2008
next sibling parent reply "Ameer Armaly" <ameer.armaly furman.edu> writes:
"Lionello Lunesu" <lionello lunesu.remove.com> wrote in message 
news:fqi8fc$241f$1 digitalmars.com...
D should establish some kind of politburo. A group of elders, each with his 
(..her?) own problem domain. Don should clearly govern the floating point 
aspects of D and just get to do what he thinks is best. : )

 To me, this 'real' problem sounds a lot like the 'int' problem: why should 
 'int' be established as the default integral type, when it is inefficient 
 on 64-bit hardware (and on 16-bit, 8-bit, .... )?

 D's very proud of having fixed sized types, and rightly so, but there 
 clearly are situations where size doesn't matter, and shouldn't matter. Of 
 course, there's a minimum limit, when any smaller would just be 
 ridiculous. ...uh... nevermind.

 So I think we should have 2 new aliases. One for a default int (at least 
 32-bit) and one for a default float (at least double). On X86/87 these new 
 aliases would map to int and real respectively.

 Coming up with good names without silly adjectives/underscores will be 
 difficult, though. I, for one, can't come up with any names that 'have it 
 in them' to replace the omnipresent 'int' and 'real'...

often do you _really_ care about the maximum value of an int? There are probably some, but more often than not I find myself wanting an integer rather than an integer at least x bits wide. Maybe we just ought to make int whatever type is fastest, and call the current int something different, maybe dword? Disclaimer: I haven't done much serious work in D recently, so odds are I'm missing something.
 L. 

Mar 03 2008
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Ameer Armaly" <ameer.armaly furman.edu> wrote in message 
news:fqiaio$29mc$1 digitalmars.com...

 I used to think fixed-size types were more optimal, but in retrospect how 
 often do you _really_ care about the maximum value of an int? There are 
 probably some, but more often than not I find myself wanting an integer 
 rather than an integer at least x bits wide. Maybe we just ought to make 
 int whatever type is fastest, and call the current int something 
 different, maybe dword?

I (and my friends) am starting to find D's choice of fixed-size int types a bit irritating since we've been writing an x86-64 kernel in D. I can't tell you how many times we've caused bugs by using int/uint instead of long/ulong. And don't even get me started on why "long" and "ulong" are awful names. (Hint: they're not correct on all platforms.) (they're normal on 64-bit.) (I'll stop now.)
Mar 03 2008
prev sibling next sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
Lionello Lunesu wrote:
sounds a lot like the 'int' problem: why
 should 'int' be established as the default integral type, when it is 
 inefficient on 64-bit hardware (and on 16-bit, 8-bit, .... )?
 
 D's very proud of having fixed sized types, and rightly so, but there 
 clearly are situations where size doesn't matter, and shouldn't matter. 
 Of course, there's a minimum limit, when any smaller would just be 
 ridiculous. ...uh... nevermind.

I agree, which I why I use ptrdiff_t for all my signed integer needs.
Mar 03 2008
prev sibling parent reply "Lionello Lunesu" <lionello lunesu.remove.com> writes:
"Christopher Wright" <dhasenan gmail.com> wrote in message 
news:fqibu4$2cfs$1 digitalmars.com...
 Lionello Lunesu wrote:
 D's very proud of having fixed sized types, and rightly so, but there 
 clearly are situations where size doesn't matter, and shouldn't matter. 
 Of course, there's a minimum limit, when any smaller would just be 
 ridiculous. ....uh... nevermind.

 So I think we should have 2 new aliases. One for a default int (at least 
 32-bit) and one for a default float (at least double). On X86/87 these 
 new aliases would map to int and real respectively.

For integers, there's size_t / hash_t.

The size_t I get, but I never understood why there's a hash_t that differs in size.
 Coming up with good names without silly adjectives/underscores will be 
 difficult, though. I, for one, can't come up with any names that 'have it 
 in them' to replace the omnipresent 'int' and 'real'...

No underscores? Faugh! You forsake your glorious C background! Fie for shame.

I've been trying to forsake my C background for a couple of years now! And it isn't easy :-S L.
Mar 03 2008
parent reply sclytrack <sclytrack pi.be> writes:
 So I think we should have 2 new aliases. One for a default int (at least
 32-bit) and one for a default float (at least double). On X86/87 these
 new aliases would map to int and real respectively.


I think D types should be int8 int32, float32 defined with a number. Things like int, float without number are all "aliases". [Another Involuntary Feature Request for Java Annotations].
Mar 04 2008
parent =?ISO-8859-1?Q?Anders_F_Bj=F6rklund?= <afb algonet.se> writes:
sclytrack wrote:

 So I think we should have 2 new aliases. One for a default int (at least
 32-bit) and one for a default float (at least double). On X86/87 these
 new aliases would map to int and real respectively.


I think D types should be int8 int32, float32 defined with a number. Things like int, float without number are all "aliases".

I'm not sure that it matters which type is the compiler built-in and which one is the alias ? At least not enough to change at this point. alias byte int8_t; alias int int32_t; But apparently it is common enough to resurface here every year... Maybe it should be entered into the FAQ, for the language design ? --anders
Mar 04 2008
prev sibling parent Don Clugston <dac nospam.com.au> writes:
Don Clugston wrote:
 The spec is not quite clear on what 'real' can be and when it should be 
 used.
 For DMD, 'real' is the 80-bit (actually 79bit) X87 extended 
 floating-point type. This has the following characteristics:
 a. it is the highest precision floating-point type supported by the 
 hardware;
 b. it is the highest precision floating-point type which is fast;
 c. it is almost as fast as double;
 d. it is the precision used internally for all floating point 
 calculations (And this makes it the only precision which is anomoly-free).
 e. it is an IEEE floating-point type.
 
 In the spec, 'real' is defined as the "largest hardware implemented 
 floating point size (Implementation Note: 80 bits for Intel CPUs)", so 
 we can apparently rely on (a); but unfortunately, that's not enough to 
 give us usage rules.

Actually, it wouldn't be necessary to create type names for all the different variations of real (except perhaps quad). A possible solution: Change the 'real' keyword into '__real'. Name mangling unchanged. Add version(X86) { alias __real real; } else { alias double real; } into std.object. (Or should the second one be a typedef? I'm not sure). Then we have the design rules: * use 'real' everywhere, except in arrays and I/O. * use __real when you want as much precision as possible, and you don't care how much precision that is. Yes, __real looks like a wierd, non-portable, OS-specific type. And that's what it is. 'real' looks like the type you should use by default.
Mar 04 2008