Jump to page: 1 25  
Page
Thread overview
Casting double to ulong weirdness
Aug 24, 2015
Márcio Martins
Aug 24, 2015
anonymous
Aug 24, 2015
Márcio Martins
Aug 24, 2015
rumbu
Aug 24, 2015
rumbu
Aug 24, 2015
Justin Whear
Aug 24, 2015
Warwick
Aug 24, 2015
Ali Çehreli
Aug 24, 2015
John Colvin
Aug 24, 2015
bachmeier
Aug 24, 2015
H. S. Teoh
Aug 24, 2015
bachmeier
Aug 24, 2015
bachmeier
Aug 24, 2015
Márcio Martins
Aug 24, 2015
Márcio Martins
Aug 24, 2015
H. S. Teoh
Aug 24, 2015
Márcio Martins
Aug 25, 2015
bachmeier
Aug 25, 2015
Márcio Martins
Aug 25, 2015
bachmeier
Aug 25, 2015
Márcio Martins
Aug 25, 2015
Matthias Bentrup
Aug 25, 2015
deadalnix
Aug 25, 2015
bachmeier
Aug 25, 2015
Warwick
Aug 25, 2015
rumbu
Aug 25, 2015
Timon Gehr
Aug 25, 2015
Timon Gehr
Aug 26, 2015
bachmeier
Aug 25, 2015
bachmeier
Aug 25, 2015
deadalnix
Aug 25, 2015
Matthias Bentrup
August 24, 2015
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)
August 24, 2015
On Monday 24 August 2015 18:52,  wrote:

> 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)

1.2 is not representable exactly in binary. Try printing it with a lot of decimal places:

writefln("%.20f", x); /* prints "1.19999999999999995559" */

Multiply that by 10: ~11.999; cast to ulong: 11.

Interestingly, printing x * 10.0 that way shows exactly 12:

writefln("%.20f", x * 10.0); /* 12.00000000000000000000 */

But cast one operand to real and you're back at 11.9...:

writefln("%.20f", cast(real)x * 10.0); /* 11.99999999999999955591 */

So, apparently, real precision is used in your code. This is not unexpected; compilers are allowed to use higher precision than requested for floating point operations. I think people have argued against it in the past, but so far Walter has been adamant about it being the right choice.
August 24, 2015
On 8/24/15 12:52 PM, "=?UTF-8?B?Ik3DoXJjaW8=?= Martins\" <marcioapm@gmail.com>\"" 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

Yes. This is part of the issue of floating point. 1.2 cannot be represented accurately.

The second case is done via real, not double, and at compile time (i.e. constant folding). There may be other reasons why this works.

You are better off adding a small epsilon:

writeln(cast(ulong)(x * 10.0 + 0.1));

> to!ulong instead of the cast does the right thing, and is a viable
> work-around.

to!ulong likely adds the epsilon, but you'd have to look to be sure.

Note, this is NOT a D problem, this is a problem with floating point. And by problem, I mean feature-that-you-should-avoid :)

-Steve
August 24, 2015
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.

std.math.round also works.
August 24, 2015
On Monday, 24 August 2015 at 17:26:12 UTC, Steven Schveighoffer wrote:
> On 8/24/15 12:52 PM, "=?UTF-8?B?Ik3DoXJjaW8=?= Martins\" <marcioapm@gmail.com>\"" 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
>
> Yes. This is part of the issue of floating point. 1.2 cannot be represented accurately.
>
> The second case is done via real, not double, and at compile time (i.e. constant folding). There may be other reasons why this works.
>
> You are better off adding a small epsilon:
>
> writeln(cast(ulong)(x * 10.0 + 0.1));
>
>> to!ulong instead of the cast does the right thing, and is a viable
>> work-around.
>
> to!ulong likely adds the epsilon, but you'd have to look to be sure.
>
> Note, this is NOT a D problem, this is a problem with floating point. And by problem, I mean feature-that-you-should-avoid :)
>
> -Steve

I am familiar with floating-point representations and their pitfalls, and I think that is not the issue here.

The issue I am trying to illustrate is the fact that the same exact operation returns different results.
Both operations are x * 10.0, except one of them passes through the stack before the cast.

I would expect this to be consistent, as I believe is the case in C/C++.
August 24, 2015
On Monday, 24 August 2015 at 17:26:12 UTC, Steven Schveighoffer wrote:
>
> Note, this is NOT a D problem, this is a problem with floating ponit. And by problem, I mean feature-that-you-should-avoid :)
>
> -Steve

Visual C++ 19.00.23026, x86, x64:

int _tmain(int argc, _TCHAR* argv[])
{
	double x = 1.2;
	printf("%d\r\n", (unsigned long long)(x * 10.0));
        double y = 1.2 * 10.0;
        printf("%d\r\n", ((unsigned long long)y));
	return 0;
}

Output:
12
12

Same output in debugger for an ARM Windows App.

C# 6.0:

static void Main(string[] args)
{
        double x = 1.2;
        WriteLine((ulong)(x * 10.0));
        double y = 1.2 * 10.0;
        WriteLine((ulong)y);
}

Output:
12
12

Same output in debugger for ARM in all flavours (Android, iOS, Windows)

It seems like a D problem.





August 24, 2015
BTW, 1.2 and 12.0 are directly representable as double

In C++:

printf("%.20f\r\n", 1.2);
printf("%.20f\r\n", 12.0);

will output:

1.20000000000000000000
12.00000000000000000000

Either upcasting to real is the wrong decision here, either the writeln string conversion is wrong.


August 24, 2015
On Mon, 24 Aug 2015 18:06:07 +0000, rumbu wrote:

> BTW, 1.2 and 12.0 are directly representable as double
> 
> In C++:
> 
> printf("%.20f\r\n", 1.2);
> printf("%.20f\r\n", 12.0);
> 
> will output:
> 
> 1.20000000000000000000 12.00000000000000000000
> 
> Either upcasting to real is the wrong decision here, either the writeln string conversion is wrong.

No it's not, this must be some sort of constant-folding or precision increase.

$ cat test.c
#include "stdio.h"

int main(int nargs, char** args)
{
		double x = 1.2;
			printf("%.20f\n", x);
}

$ clang test.c && ./a.out
1.19999999999999995559
August 24, 2015
On 08/24/2015 11:06 AM, rumbu wrote:

> BTW, 1.2 and 12.0 are directly representable as double

12 is but 1.2 is not.

> In C++:
>
> printf("%.20f\r\n", 1.2);
> printf("%.20f\r\n", 12.0);
>
> will output:
>
> 1.20000000000000000000
> 12.00000000000000000000
>
> Either upcasting to real is the wrong decision here, either the writeln
> string conversion is wrong.

Output is one thing. The issue is with the representation of 1.2. You need infinite digits. D's %a helps with visualizing it:

import std.stdio;

void main()
{
    writefln("%a", 1.2);
    writefln("%a", 12.0);
}

Outputs

0x1.3333333333333p+0
0x1.8p+3

Ali

August 24, 2015
On Monday, 24 August 2015 at 18:06:08 UTC, rumbu wrote:
> BTW, 1.2 and 12.0 are directly representable as double

12.0 is representable, but I'm pretty sure, if you work it out, 1.2 isn't.
« First   ‹ Prev
1 2 3 4 5