Casting double to ulong weirdness

via Digitalmars-d digitalmars-d at puremagic.com
Mon Aug 24 13:34:51 PDT 2015


On Monday, 24 August 2015 at 19:23:44 UTC, Steven Schveighoffer 
wrote:
> On 8/24/15 1:43 PM, bachmeier wrote:
>> On Monday, 24 August 2015 at 16:52:54 UTC, Márcio Martins 
>> wrote:
>>> I'm posting this here for visibility. This was silently 
>>> corrupting our
>>> data, and might be doing the same for others as well.
>>>
>>> import std.stdio;
>>> void main() {
>>>   double x = 1.2;
>>>   writeln(cast(ulong)(x * 10.0));
>>>   double y = 1.2 * 10.0;
>>>   writeln(cast(ulong)y);
>>> }
>>>
>>> Output:
>>> 11
>>> 12
>>>
>>>
>>> to!ulong instead of the cast does the right thing, and is a 
>>> viable
>>> work-around.
>>>
>>> Issue: https://issues.dlang.org/show_bug.cgi?id=14958)
>>
>> I would not describe to!ulong as a "work-around". You just 
>> discovered
>> one of the reasons to! exists: it is the right way to do it and
>> cast(ulong) is the wrong way. As the others have noted, 
>> floating point
>> is tricky business, and you need to use the right tools for 
>> the job.
>
> real y = x * 10.0;
> writeln(y.to!ulong); // 11
>
> to! does not do anything different than cast. What is happening 
> here is the implicit cast from real to double. D treats the 
> result of x * 10.0 as type double, but it's done at real 
> precision. In that conversion, the error is hidden by a 
> rounding automatically done by the processor I think.
>
> -Steve

Whatever the issue is, it is not unavoidable, because as has been 
shown, other languages do it correctly.

 From the data presented so far, it seems like the issue is that 
the mul is performed in 80-bit precision, storing it before the 
cast forces a truncation down to 64-bit. Similarly, passing it to 
a function will also truncate to 64-bit, due to ABIs. This is why 
to! works as expected.

Please do keep in mind that the issue is not one of precision, 
but one of inconsistency. They are not the same thing. The result 
being 11 or 12 is irrelevant to this issue. It should just be the 
same for two instances of the same expression.

In an attempt to make things more obvious, consider this example, 
which also illustrates why to! works, despite apparently doing 
nothing extra at all.

double noop(double z) {
   return z;
}

void main() {
   double x = 1.2;
   writeln(cast(ulong)(x * 10.0));
   writeln(cast(ulong)noop(x * 10.0));
}

Outputs:
11
12


More information about the Digitalmars-d mailing list