www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Three floating point questions

reply bearophile <bearophileHUGS lycos.com> writes:
This program prints (dmd 2.048):
7ff4000000000000
7ffc000000000000

// program #1
import std.stdio: writeln, writefln;
import std.string: format;
void main() {
    string toHex(T)(T x) {
        string result;
        ubyte* ptr = cast(ubyte*)&x;
        foreach (i; 0 .. T.sizeof)
            result = format("%02x", ptr[i]) ~ result;
        return result;
    }
    writeln(toHex(double.init));
    writefln("%x", cast(ulong)double.init);
}

Do you know what cast(ulong) is doing here?

---------------------------

This program prints (dmd 2.048)::
-nan

// program #2
import std.stdio: writeln;
void main() {
    writeln(0.0 / 0.0);
}

Is it a bug of writeln?

---------------------------

Do you know why this isn't raising runtime errors?

// program #3
import std.math: FloatingPointControl;
import std.c.stdlib: atof;
void main() {
    double d3 = atof("3.0");
    double x; // nan
    FloatingPointControl fpc;
    fpc.enableExceptions(FloatingPointControl.severeExceptions);
    double r = x * d3;
}

Bye,
bearophile
Aug 25 2010
parent reply Don <nospam nospam.com> writes:
bearophile wrote:
 This program prints (dmd 2.048):
 7ff4000000000000
 7ffc000000000000
 
 // program #1
 import std.stdio: writeln, writefln;
 import std.string: format;
 void main() {
     string toHex(T)(T x) {
         string result;
         ubyte* ptr = cast(ubyte*)&x;
         foreach (i; 0 .. T.sizeof)
             result = format("%02x", ptr[i]) ~ result;
         return result;
     }
     writeln(toHex(double.init));
     writefln("%x", cast(ulong)double.init);
 }
 
 Do you know what cast(ulong) is doing here?

Turning it from a signalling nan to a quiet nan.
 ---------------------------
 
 This program prints (dmd 2.048)::
 -nan
 
 // program #2
 import std.stdio: writeln;
 void main() {
     writeln(0.0 / 0.0);
 }
 
 Is it a bug of writeln?

You mean, because it's a negative nan?
 ---------------------------
 
 Do you know why this isn't raising runtime errors?
 
 // program #3
 import std.math: FloatingPointControl;
 import std.c.stdlib: atof;
 void main() {
     double d3 = atof("3.0");
     double x; // nan
     FloatingPointControl fpc;
     fpc.enableExceptions(FloatingPointControl.severeExceptions);
     double r = x * d3;
 }

That's a known bug in the backend, which I still haven't fixed. The signalling nans get triggered in the 'double x; ' line. This happens because there's a difference in the way AMD and Intel deal with signalling nans, which is completely unpublicised. So my initial testing was inadequate.
Aug 25 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Don:
 Do you know what cast(ulong) is doing here?

Turning it from a signalling nan to a quiet nan.

I really really didn't know this. Is this written somewhere in the D docs? :-)
 You mean, because it's a negative nan?

Yes, I mean that. Is a negative nan a meaningful concept? Is it a true negative nan coming from the division of two positive values, or it's just a bug of writeln, or is it something completely different?
 The signalling nans get triggered in the 'double x; ' line.

(You are often two steps forward compared to my thought patterns, so please be patient with me) In this program the FP register is modified after that definition line, so I don't expect 'double x; ' to trigger an hardware exception. But the 'double r = x * d3;' line computes the product of a signaling nan and a normal double, so this is an operation that has to produce an error, I think.
 This happens because there's a difference in the way AMD and Intel deal 
 with signalling nans, which is completely unpublicised. So my initial 
 testing was inadequate.

I see. If not even official CPU specs show such data, then not being right in the first (and second) implementation is forgiveable, I presume :o) Bye and thank you, bearophile
Aug 25 2010
parent Don <nospam nospam.com> writes:
bearophile wrote:
 Don:
 Do you know what cast(ulong) is doing here?


I really really didn't know this. Is this written somewhere in the D docs? :-)

It's the way signalling nans work. _Any_ use of them raises a floating point exception, then turns them into a quiet NaN.
 You mean, because it's a negative nan?

Yes, I mean that. Is a negative nan a meaningful concept?

It's part of the payload of the nan. Is it a true negative nan coming from the division of two positive values, or it's just a bug of writeln, or is it something completely different?
 
 
 The signalling nans get triggered in the 'double x; ' line.

(You are often two steps forward compared to my thought patterns, so please be patient with me) In this program the FP register is modified after that definition line, so I don't expect 'double x; ' to trigger an hardware exception.

Unfortunately the way the backend deals with it at present is to do: double x; x = double.init; which is wrong because it involves an assignment, which may or may not trigger a floating point exception, depending on your cpu*. If it triggers one, then x contains a quiet nan, instead of a signalling one! So the signalling nan idea doesn't quite work yet. * it actually is documented in Intel/AMD docs for the FLD and FSTP instructions, but you have to read it _very_ carefully to notice. As far as I can tell, nobody has noticed the difference before. I only discovered it through experiment.
Aug 26 2010