www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - An unfortunate quirk of DMD32 converting floating-point to string

reply Ivan Kazmenko <gassa mail.ru> writes:
While exploring quirks of floating-point values, as well as 
C/C++/D convenience with them, I stumbled on, in essence, the 
following (DMD32 on Windows):

void main ()
{
     import std.stdio : writefln;
     double x = 128.0;        // same for real or float
     writefln ("%.20a", x);   // 0x1.00000000000000000000p+7, right
     writefln ("%.20f", x);   // 127.99999999999999999000, wrong
}

1. The internal representation is fine: the exponent (before 
shift) is 7, and the mantissa is all-zeroes (except the "1." part 
which is not stored).

2. Formatting to a decimal representation goes off in a bad way, 
giving the wrong third significant digit.

3. The trail of 9s is the same ~20 decimal digits for every 
floating-point type, which suggests that they are all converted 
to 80-bit real before formatting.  This obscures their difference 
in width, which is bad from at least the learning standpoint.

Point 2 is sad.  One would expect at least the exact powers of 
two to be stored exactly.  And indeed they are.  But trying to 
demonstrate it gives the wrong impression that they are not.  
This adds unnecessary confusion to the already complex subject of 
how floating-point values work, and learning the subject with D 
becomes much harder.

On the competition front, this already seems to be settled: at 
least with MinGW GCC, both C printf and C++ cout correctly 
display powers of two - and perhaps any small integers exactly 
stored as floating-point data, for that matter.

With DMD64, the issue vanishes.  This strongly suggests that 
32-bit druntime is the culprit.  And indeed, the Phobos 
"formatValue ... if (is(FloatingPointTypeOf!T) && ...)" for 
floating-point calls snprintf from druntime, after which I 
couldn't easily track it to the source code.  Should this be so?  
And regardless, perhaps snprintf (or Phobos or whatever will do 
the dirty work) can adapt a more modern approach so that integers 
in floating-point don't get corrupted when converted to string 
representation?  Perhaps even without sacrificing much speed.  
The most obvious way seems to just make multiplications by 10, 
comparisons and subtractions in a loop, and is likely slow but at 
least correct for integers.

I'm going to make this a bugreport, but first wanted to 
explicitly point at this in the discussion group, since it's 
quirky, and I may have easily missed something important.

Ivan Kazmenko.
Dec 26 2017
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 12/26/2017 3:41 PM, Ivan Kazmenko wrote:
 While exploring quirks of floating-point values, as well as C/C++/D
convenience 
 with them, I stumbled on, in essence, the following (DMD32 on Windows):
The issue is really with the DMC++ C runtime library, as that is what Phobos relies on for floating point formatting.
Dec 26 2017
parent Ivan Kazmenko <gassa mail.ru> writes:
On Tuesday, 26 December 2017 at 23:58:43 UTC, Walter Bright wrote:
 On 12/26/2017 3:41 PM, Ivan Kazmenko wrote:
 While exploring quirks of floating-point values, as well as 
 C/C++/D convenience with them, I stumbled on, in essence, the 
 following (DMD32 on Windows):
The issue is really with the DMC++ C runtime library, as that is what Phobos relies on for floating point formatting.
In the issue tracker: https://issues.dlang.org/show_bug.cgi?id=18139
Dec 28 2017