Casting double to ulong weirdness
via Digitalmars-d
digitalmars-d at puremagic.com
Mon Aug 24 14:34:22 PDT 2015
On Monday, 24 August 2015 at 21:03:50 UTC, Steven Schveighoffer
wrote:
> On 8/24/15 4:34 PM, "=?UTF-8?B?Ik3DoXJjaW8=?= Martins\"
> <marcioapm at gmail.com>\"" wrote:
>> 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.
>>
>> Whatever the issue is, it is not unavoidable, because as has
>> been shown,
>> other languages do it correctly.
>
> Your other examples use doubles, not reals. It's not apples to
> apples.
All my examples are doubles, and I have tested them all in C++ as
well, using doubles. It is indeed apples to apples :)
>
>> 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.
>
> Not just truncation, rounding too.
What? If rounding was performed, then it would work as expected.
i.e. both outputs would be 12.
>
>> 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.
>
> It is an issue of precision. In order to change from real to
> double, some bits must be lost. Since certain numbers cannot be
> represented, the CPU must round or truncate.
There is no mention of real anywhere in any code. The intent is
clearly stated in the code and while I accept precision and
rounding errors, especially because DMD has no way to select a
floating point model, that I am aware of, at least, it's very
hard for me to accept the 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.
>
> They are not the same expression. One goes from double through
> multiplication to real, then back to double, then to ulong. The
> other skips the real to double conversion and goes directly to
> ulong.
There is only 1 floating-point operation and one cast per
expression. They are effectively the same except one value is
stored in a temporary before casting. The intent expressed in the
code is absolutely the same. All values are the same, operation
order is the same, and types are all the same.
>
> The real issue here is that you are not correctly converting
> from a floating point number to an integer.
>
>> 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
>
> I understand the inconsistency, and I agree it is an issue that
> should be examined. But the issue is entirely avoidable by not
> using incorrect methods to convert from floating point to
> integer after floating point operations introduce some small
> level of error.
>
> Perhaps there is some way to make it properly round in this
> case, but I guarantee it will not fix all floating point errors.
>
> -Steve
What is the correct way to truncate, not round, a floating-point
value to an integer?
More information about the Digitalmars-d
mailing list