www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Force usage of double (instead of higher precision)

reply Simon =?UTF-8?B?QsO8cmdlcg==?= <simon.buerger rwth-aachen.de> writes:
According to the standard (http://dlang.org/spec/float.html), the 
compiler is allowed to compute any floating-point statement in a 
higher precision than specified. Is there a way to deactivate 
this behaviour?

Context (reason why I need this): I am building a "double double" 
type, which essentially takes two 64-bit double-precision numbers 
to emulate a (nearly) quadruple-precision number. A simplified 
version looks something like this:

struct ddouble
{
     double high;
     double low;

     invariant
     {
         assert(high + low == high);
     }

     // ...implemententations of arithmetic operations...
}

Everything works fine at run-time, but if I declare a 
compile-time constant like

enum pi = ddouble(3.141592653589793116e+00, 
1.224646799147353207e-16);

the invariant fails because it is evaluated using 80-bit 
"extended precision" during CTFE. All arithmetic operations rely 
on IEEE-conform double-precision, so everything breaks down if 
the compiler decides to replace them with higher precision. I am 
currently using LDC on 64-bit-Linux if that is relevant.

(If you are interested in the "double double" type, take a look 
here:
https://github.com/BrianSwift/MetalQD
which includes a double-double and even quad-double 
implementation in C/C++/Fortran)
Jun 28 2017
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 28 June 2017 at 22:16:48 UTC, Simon Bürger wrote:

 (If you are interested in the "double double" type, take a look 
 here:
 https://github.com/BrianSwift/MetalQD
 which includes a double-double and even quad-double 
 implementation in C/C++/Fortran)
Nice work can you re or dual license under the boost license ? I'd like to incorporate the qd type into newCTFE. As for your problems they can be worked around. by assigning every temporary to a variable. Which will enforce the discarding of precision.
Jun 28 2017
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 28 June 2017 at 23:56:42 UTC, Stefan Koch wrote:
 As for your problems they can be worked around.
 by assigning every temporary to a variable.
 Which will enforce the discarding of precision.
Sorry for the misinformation! I was using the newCTFE fork which fixes this. Indeed you'll have no way to get rid of the excess precision except for creating a function per sub-expression.
Jun 28 2017
parent reply Simon =?UTF-8?B?QsO8cmdlcg==?= <simon.buerger rwth-aachen.de> writes:
Thanks a lot for your comments.

On Wednesday, 28 June 2017 at 23:56:42 UTC, Stefan Koch wrote:
 [...]

 Nice work can you re or dual license under the boost license ?
 I'd like to incorporate the qd type into newCTFE.
The original work is not mine but traces back to http://crd-legacy.lbl.gov/~dhbailey/mpdist/ which is under a (modified) BSD license. I just posted the link for context, sorry for the confusion. Doing a port to D does not allow me to change the license even though I not a single line from the original would remain (I think?). I might do a completely new D implementation (still based on the original authors research paper, not on the details of their code). But 1. I probably would only do a subset of functions I need for my work (i.e. double-double only, no quad-double, and only a limited set of trancendental functions). 2. Given that I have seen the original code, this might still be considered a "derivative work". I'm not sure, copyright-law is kinda confusing to me in these cases.
 Indeed you'll have no way to get rid of the excess precision 
 except for creating a function per sub-expression.
No, doesn't seem to work. Here is a minimal breaking example: double sum(double x, double y) { return x + y; } bool equals(double x, double y) { return x == y; } enum pi = ddouble(3.141592653589793116e+00, 1.224646799147353207e-16); struct ddouble { double hi, lo; invariant { if(!isNaN(hi) && !isNaN(lo)) assert(equals(sum(hi, lo), hi)); } this(double hi, double lo) { this.hi = hi; this.lo = lo; } } But there are workarounds that seem to work: 1. remove the constructor (I think this means the invariant is not checked anymore?) 2. disable the invariant in ctfe (using "if(__ctfe) return;") 3. Don't use any ctfe (by replacing enum with immutable globals, initialized in "static this").
 I was using the newCTFE fork which fixes this.
Does this mean your new CTFE code (which is quite impressive work as far as I can tell), floating point no longer gets promoted to higher precision? That would be really good news for hackish floating-point code. Honestly, this whole "compiler gets to decide which type to actually use" thing really bugs me. Kinda reminiscent of C/C++ integer types which could in principle be anything at all. I thought D had fixed this by specifying "int = 32-bit, long = 64-bit, float = IEEE-single-precision, double = IEEE-double-precision". Apparently not. If I write "double", I would like to get IEEE-conform double-precision operations. If I wanted something depending on target-platform and compiler-optimization-level I would have used "real". Also this 80-bit-extended type is just a bad idea in general and should never be used (IMHO). Even on x86 processors, it only exists for backward-compatibility. No current instruction set (like SEE/AVX) supports it. Sorry for the long rant. But I am puzzled that the spec (https://dlang.org/spec/float.html) actually encourages double<->real convertions while at the same time it (rightfully) disallows "unsafe math optimizations" such as "x-x=0".
Jun 29 2017
parent reply Luis <luis.panadero gmail.com> writes:
On Thursday, 29 June 2017 at 12:00:53 UTC, Simon Bürger wrote:
 Thanks a lot for your comments.

 On Wednesday, 28 June 2017 at 23:56:42 UTC, Stefan Koch wrote:
 [...]
This is only happening on CTFE ? Enforcing to use the old 8086 FPU for any float/double operation would give a lot performance penalty on any modern CPU.
Jun 30 2017
parent reply kinke <noone nowhere.com> writes:
On Friday, 30 June 2017 at 11:42:39 UTC, Luis wrote:
 On Thursday, 29 June 2017 at 12:00:53 UTC, Simon Bürger wrote:
 Thanks a lot for your comments.

 On Wednesday, 28 June 2017 at 23:56:42 UTC, Stefan Koch wrote:
 [...]
This is only happening on CTFE ? Enforcing to use the old 8086 FPU for any float/double operation would give a lot performance penalty on any modern CPU.
CTFE only (incl. parsing of literals). Just make sure you don't happen to call a std.math function only accepting reals; I don't know how many of those are still around.
Jun 30 2017
parent reply kinke <noone nowhere.com> writes:
On Friday, 30 June 2017 at 16:21:18 UTC, kinke wrote:
 CTFE only (incl. parsing of literals). Just make sure you don't 
 happen to call a std.math function only accepting reals; I 
 don't know how many of those are still around.
Oh, apparently most still are. There are even some mean overloads for double/float only casting and forwarding to the `real` implementation.
Jun 30 2017
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Friday, 30 June 2017 at 16:29:22 UTC, kinke wrote:
 On Friday, 30 June 2017 at 16:21:18 UTC, kinke wrote:
 CTFE only (incl. parsing of literals). Just make sure you 
 don't happen to call a std.math function only accepting reals; 
 I don't know how many of those are still around.
Oh, apparently most still are. There are even some mean overloads for double/float only casting and forwarding to the `real` implementation.
That is not a problem. The only problem is that the excess is not discarded at the end. Which is because of the design of the constant-folder.
Jun 30 2017
parent kinke <noone nowhere.com> writes:
On Friday, 30 June 2017 at 16:39:16 UTC, Stefan Koch wrote:
 On Friday, 30 June 2017 at 16:29:22 UTC, kinke wrote:
 On Friday, 30 June 2017 at 16:21:18 UTC, kinke wrote:
 CTFE only (incl. parsing of literals). Just make sure you 
 don't happen to call a std.math function only accepting 
 reals; I don't know how many of those are still around.
Oh, apparently most still are. There are even some mean overloads for double/float only casting and forwarding to the `real` implementation.
That is not a problem. The only problem is that the excess is not discarded at the end. Which is because of the design of the constant-folder.
I was referring to runtime performance, as that was what Luis was concerned about.
Jun 30 2017
prev sibling parent reply kinke <noone nowhere.com> writes:
On Wednesday, 28 June 2017 at 22:16:48 UTC, Simon Bürger wrote:
 I am currently using LDC on 64-bit-Linux if that is relevant.
It is, as LDC on Windows/MSVC would use 64-bit compile-time reals. ;) Changing it to double on other platforms is trivial if you compile LDC yourself. You'll want to use this alias: https://github.com/ldc-developers/ldc/blob/master/ddmd/root/ctfloat.d#L19, https://github.com/ldc-developers/ldc/blob/master/ddmd/root/ctfloat.h#L19
Jun 28 2017
parent reply Simon =?UTF-8?B?QsO8cmdlcg==?= <simon.buerger rwth-aachen.de> writes:
On Thursday, 29 June 2017 at 00:07:35 UTC, kinke wrote:
 On Wednesday, 28 June 2017 at 22:16:48 UTC, Simon Bürger wrote:
 I am currently using LDC on 64-bit-Linux if that is relevant.
It is, as LDC on Windows/MSVC would use 64-bit compile-time reals. ;) Changing it to double on other platforms is trivial if you compile LDC yourself. You'll want to use this alias: https://github.com/ldc-developers/ldc/blob/master/ddmd/root/ctfloat.d#L19, https://github.com/ldc-developers/ldc/blob/master/ddmd/root/ctfloat.h#L19
Huh, I will definitely look into this. This might be the most elegant solution. Thanks for the suggestion.
Jun 29 2017
parent Stefan Koch <uplink.coder googlemail.com> writes:
On Thursday, 29 June 2017 at 12:02:48 UTC, Simon Bürger wrote:
 On Thursday, 29 June 2017 at 00:07:35 UTC, kinke wrote:
 On Wednesday, 28 June 2017 at 22:16:48 UTC, Simon Bürger wrote:
 I am currently using LDC on 64-bit-Linux if that is relevant.
It is, as LDC on Windows/MSVC would use 64-bit compile-time reals. ;) Changing it to double on other platforms is trivial if you compile LDC yourself. You'll want to use this alias: https://github.com/ldc-developers/ldc/blob/master/ddmd/root/ctfloat.d#L19, https://github.com/ldc-developers/ldc/blob/master/ddmd/root/ctfloat.h#L19
Huh, I will definitely look into this. This might be the most elegant solution. Thanks for the suggestion.
Required a custom build of the compiler for the library to work is rather inelegant imo. However It might allow you to make progress the quickest. Note that this only changes the type all ct-float-math is done at to double. which means that know float and real will be double. (Which is probably better then having float and double transform into 80bit reals)
Jun 29 2017