August 24, 2015
On 8/24/15 5:03 PM, Steven Schveighoffer wrote:
>> 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.

#include <stdio.h>

int main()
{
    long double x = 1.2;
    x *= 10.0;
    printf("%lld\n", (unsigned long long)x);
}

output:
11

-Steve
August 24, 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@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?
August 24, 2015
On Mon, Aug 24, 2015 at 09:34:22PM +0000, via Digitalmars-d wrote: [...]
> What is the correct way to truncate, not round, a floating-point value to an integer?

std.math.trunc.


T

-- 
Having a smoking section in a restaurant is like having a peeing section in a swimming pool. -- Edward Burr
August 24, 2015
On Monday, 24 August 2015 at 22:12:42 UTC, H. S. Teoh wrote:
> On Mon, Aug 24, 2015 at 09:34:22PM +0000, via Digitalmars-d wrote: [...]
>> What is the correct way to truncate, not round, a floating-point value to an integer?
>
> std.math.trunc.
>
>
> T

import std.stdio;
import std.math;

void main() {
	double x = 1.2;
	writeln(std.math.trunc(x * 10.0));

	double y = x * 10.0;
	writeln(std.math.trunc(y));
}

Outputs:
11
12
August 25, 2015
On Monday, 24 August 2015 at 21:34:23 UTC, Márcio Martins wrote:
>>> Whatever the issue is, it is not unavoidable, because as has been shown,
>>> other languages do it correctly.

There's no guarantee that it will be done consistently or correctly in C or C++ to my knowledge. Some compilers will do it consistently, but it's absolutely not portable.

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

It's fully consistent with what DMD claims to do:

http://dlang.org/portability.html

While a compiler can guarantee consistency, I don't know of any way to guarantee correctness, which makes the question of consistency irrelevant. There's no way to know what will happen when you run the program.

> What is the correct way to truncate, not round, a floating-point value to an integer?

If you can be an epsilon above or below the exact answer, there's no way to guarantee correctness unless you know you're not doing something that resembles integer operations. If the exact answer is 12.2 or 12.6, you can do it correctly. If it is 12.0 or 23.0, you can get the wrong answer.
August 25, 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)

http://www.smbc-comics.com/?id=2999
August 25, 2015
On 8/24/15 5:34 PM, "=?UTF-8?B?Ik3DoXJjaW8=?= Martins\" <marcioapm@gmail.com>\"" wrote:
> On Monday, 24 August 2015 at 21:03:50 UTC, Steven Schveighoffer wrote:

>> 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.
>>
>
> What is the correct way to truncate, not round, a floating-point value
> to an integer?

auto result = cast(ulong)(x * 10.0 + x.epsilon);

-Steve
August 25, 2015
On Tuesday, 25 August 2015 at 11:14:35 UTC, Steven Schveighoffer wrote:
> On 8/24/15 5:34 PM, "=?UTF-8?B?Ik3DoXJjaW8=?= Martins\" <marcioapm@gmail.com>\"" wrote:
>> On Monday, 24 August 2015 at 21:03:50 UTC, Steven Schveighoffer wrote:
>
>>> 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.
>>>
>>
>> What is the correct way to truncate, not round, a floating-point value
>> to an integer?
>
> auto result = cast(ulong)(x * 10.0 + x.epsilon);
>
> -Steve

import std.stdio;
void main() {
	double x = 1.2;
	writeln(cast(ulong)(x * 10.0 + x.epsilon));

	double y = x * 10.0;
	writeln(cast(ulong)(y + x.epsilon));
	
	double z = x * 10.0 + x.epsilon;
	writeln(cast(ulong)(z));
}

Outputs:
11
12
12

I leave it at this. It seems like this only bothers me, and I have no more time to argue.
The workaround is not that bad, and at the end of the day, it is just one more thing on the list.
August 25, 2015
On Tuesday, 25 August 2015 at 11:14:35 UTC, Steven Schveighoffer wrote:
> On 8/24/15 5:34 PM, "=?UTF-8?B?Ik3DoXJjaW8=?= Martins\" <marcioapm@gmail.com>\"" wrote:
>> On Monday, 24 August 2015 at 21:03:50 UTC, Steven Schveighoffer wrote:
>
>>> 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.
>>>
>>
>> What is the correct way to truncate, not round, a floating-point value
>> to an integer?
>
> auto result = cast(ulong)(x * 10.0 + x.epsilon);
>
> -Steve

That will work in this case (or maybe not, as Marcio's other post shows) but it's still not a general solution. You're imposing the assumption that anything sufficiently close to an integer value is that integer. Truncating a floating point number is not a well-defined exercise because you only know an interval that holds the true value.
August 25, 2015
On Tuesday, 25 August 2015 at 13:51:18 UTC, Márcio Martins wrote:

> import std.stdio;
> void main() {
> 	double x = 1.2;
> 	writeln(cast(ulong)(x * 10.0 + x.epsilon));
>
> 	double y = x * 10.0;
> 	writeln(cast(ulong)(y + x.epsilon));
> 	
> 	double z = x * 10.0 + x.epsilon;
> 	writeln(cast(ulong)(z));
> }
>
> Outputs:
> 11
> 12
> 12
>
> I leave it at this. It seems like this only bothers me, and I have no more time to argue.
> The workaround is not that bad, and at the end of the day, it is just one more thing on the list.

What you are attempting to do is impossible. Is there a reason you can't use std.math.round, which is the tool that was made for the task?