Discussion:
Bug?
deed via Digitalmars-d-learn
2014-10-23 17:08:36 UTC
Permalink
// DMD v2.066.0
// All asserts pass (!)

import std.math : sin, cos, tan, asin, acos, atan,
sinh, cosh, tanh, asinh, acosh, atanh;

alias F = double;

immutable F a = 3, b = 5;

F fmul (F a) pure { return a * b; }
F fsin (F a) pure { return sin(a); }

struct Smul { F value; this (F a) { value = a * b; } alias value
this; }
struct Ssin { F value; this (F a) { value = sin(a); } alias value
this; }

F ans_mul = fmul(a);
F ans_sin = fsin(a);

Smul smul = Smul(a);
Ssin ssin = Ssin(a);


// All combinations of a*b, fmul(a), Smul(a), ans_mul and smul
pass
// the equality test. E.g.:
assert (a*b == a*b);
assert (a*b == fmul(a));
assert (a*b == Smul(a));
assert (a*b == ans_mul);
assert (a*b == smul);

assert (fmul(a) == fmul(a));
assert (fmul(a) == Smul(a));
assert (fmul(a) == ans_mul);
assert (fmul(a) == smul);


// However, for std.math.sin it's different:
assert (sin(a) == fsin(a)); // But not in 2.065
assert (sin(a) != Ssin(a)); // ?
assert (sin(a) != ans_sin); // ?
assert (sin(a) != ssin); // ?

assert (fsin(a) != fsin(a)); // ?
assert (fsin(a) != Ssin(a)); // ?
assert (fsin(a) != ans_sin); // ?
assert (fsin(a) != ssin); // ?

// Same goes for cos, tan, asin, acos, atan:
F fcos (F a) { return cos(a); }
F ftan (F a) { return tan(a); }
F fasin (F a) { return asin(a); }
F facos (F a) { return acos(a); }
F fatan (F a) { return atan(a); }

assert (fcos(a) != fcos(a)); // ?
assert (ftan(a) != ftan(a)); // ?
assert (fasin(a) != fasin(a)); // ?
assert (facos(a) != facos(a)); // ?
assert (fatan(a) != fatan(a)); // ?


// And then it goes only downhill for
// sinh, cosh, tanh, asinh, acosh and atanh:
assert (sinh(a) != sinh(a)); // ?
assert (cosh(a) != cosh(a)); // ?
assert (tanh(a) != tanh(a)); // ?
assert (asinh(a) != asinh(a)); // ?
assert (acosh(a) != acosh(a)); // ?
assert (atanh(0.5) != atanh(0.5)); // ?

--

Why bother?

import std.algorithm : max;

F fun (F a, F b) { return max(a,b) + 1.; }
unittest { assert (gun(1, 2) == gun(2, 1)); } // Passes

F pun (F a, F b) { return sin(max(a,b)); }
unittest { assert (fun(1, 2) == fun(2, 1)); } // Fails
deed via Digitalmars-d-learn
2014-10-23 17:11:09 UTC
Permalink
Post by deed via Digitalmars-d-learn
--
Why bother?
import std.algorithm : max;
F fun (F a, F b) { return max(a,b) + 1.; }
unittest { assert (gun(1, 2) == gun(2, 1)); } // Passes
F pun (F a, F b) { return sin(max(a,b)); }
unittest { assert (fun(1, 2) == fun(2, 1)); } // Fails
// Fun, gun, pun...
unittest { assert (fun(1, 2) == fun(2, 1)); } // Passes
unittest { assert (pun(1, 2) == pun(2, 1)); } // Fails
Steven Schveighoffer via Digitalmars-d-learn
2014-10-23 17:33:22 UTC
Permalink
Post by deed via Digitalmars-d-learn
// DMD v2.066.0
// All asserts pass (!)
Using equality is not a good idea with floating point.

The compiler will on a whim, or depending on whether it can inline or
not, use higher precision floats, changing the outcome slightly.

I cannot say for certain whether this explains all the issues you have,
the very last one seems troubling to me at least.

-Steve
deed via Digitalmars-d-learn
2014-10-23 18:18:36 UTC
Permalink
Post by Steven Schveighoffer via Digitalmars-d-learn
Using equality is not a good idea with floating point.
The compiler will on a whim, or depending on whether it can
inline or not, use higher precision floats, changing the
outcome slightly.
I cannot say for certain whether this explains all the issues
you have, the very last one seems troubling to me at least.
-Steve
Sure, in many cases it's a bad idea. While I understand that
sin(PI) != 0.0, but approxEqual(sin(PI), 0.0) == true, I would
expect the following to pass:

assert (0.0 == 0.0);
assert (1.2345 == 1.2345);

F a = 1.2345, b = 9.8765;

assert (a+b == b+a);
assert (a*b == b*a);

F fun (F a) pure;

assert (fun(a) + fun(b) == fun(b) + fun(a));
assert (fun(a) * fun(b) == fun(b) * fun(a));

auto a = fun(100);
auto b = fun(100);

assert (a == b);
assert (fun(100) == fun(100));


Now, if fun's body is { return sin(a); }, the behaviour changes
to:

auto c = fun(100);
auto d = fun(100);

assert (c == d); // Ok
assert (fun(100) != fun(100)) // I have a hard time understanding
// this is correct behaviour
Steven Schveighoffer via Digitalmars-d-learn
2014-10-23 18:26:52 UTC
Permalink
Post by Steven Schveighoffer via Digitalmars-d-learn
Using equality is not a good idea with floating point.
The compiler will on a whim, or depending on whether it can inline or
not, use higher precision floats, changing the outcome slightly.
I cannot say for certain whether this explains all the issues you
have, the very last one seems troubling to me at least.
Sure, in many cases it's a bad idea. While I understand that sin(PI) !=
0.0, but approxEqual(sin(PI), 0.0) == true, I would expect the following
assert (0.0 == 0.0);
assert (1.2345 == 1.2345);
F a = 1.2345, b = 9.8765;
assert (a+b == b+a);
assert (a*b == b*a);
None of these fail on my system
F fun (F a) pure;
assert (fun(a) + fun(b) == fun(b) + fun(a));
assert (fun(a) * fun(b) == fun(b) * fun(a));
auto a = fun(100);
auto b = fun(100);
assert (a == b);
assert (fun(100) == fun(100));
Not sure what body of fun is, so I cannot test this.
auto c = fun(100);
auto d = fun(100);
assert (c == d); // Ok
assert (fun(100) != fun(100)) // I have a hard time understanding
// this is correct behaviour
Tried that out, it does not fail on my machine. Can you be more specific
on your testing? What compiler/platform? Stock compiler, or did you
build it yourself?

-Steve
deed via Digitalmars-d-learn
2014-10-23 18:46:07 UTC
Permalink
On Thursday, 23 October 2014 at 18:26:53 UTC, Steven
Post by Steven Schveighoffer via Digitalmars-d-learn
Post by deed via Digitalmars-d-learn
Post by Steven Schveighoffer via Digitalmars-d-learn
Using equality is not a good idea with floating point.
The compiler will on a whim, or depending on whether it can
inline or
not, use higher precision floats, changing the outcome
slightly.
I cannot say for certain whether this explains all the issues you
have, the very last one seems troubling to me at least.
Sure, in many cases it's a bad idea. While I understand that
sin(PI) !=
0.0, but approxEqual(sin(PI), 0.0) == true, I would expect the following
assert (0.0 == 0.0);
assert (1.2345 == 1.2345);
F a = 1.2345, b = 9.8765;
assert (a+b == b+a);
assert (a*b == b*a);
None of these fail on my system
Same for me, that's the point; it's perfectly valid to compare
floating points.
Post by Steven Schveighoffer via Digitalmars-d-learn
Post by deed via Digitalmars-d-learn
F fun (F a) pure;
assert (fun(a) + fun(b) == fun(b) + fun(a));
assert (fun(a) * fun(b) == fun(b) * fun(a));
auto a = fun(100);
auto b = fun(100);
assert (a == b);
assert (fun(100) == fun(100));
Not sure what body of fun is, so I cannot test this.
Could be anything taking a floating point and returning a
floating point.
You would expect to get the same back for the same input,
especially when
it's pure. If so, the asserts above are expected to hold.
Post by Steven Schveighoffer via Digitalmars-d-learn
Post by deed via Digitalmars-d-learn
Now, if fun's body is { return sin(a); }, the behaviour
auto c = fun(100);
auto d = fun(100);
assert (c == d); // Ok
assert (fun(100) != fun(100)) // I have a hard time
understanding
// this is correct behaviour
Tried that out, it does not fail on my machine. Can you be more
specific on your testing? What compiler/platform? Stock
compiler, or did you build it yourself?
Right. It doesn't fail, and that's the problem.

assert (fun(100) == fun(100)); // Should pass, and does with
// body { return a + 1; }
// but not with
// body { return sin(a); }
assert (fun(100) != fun(100)); // Shouldn't pass, but passes with
// body { return sin(a);}

Compiler: DMD32 D Compiler v2.066.0

Also tried dpaste.dzfl.pl with 2.065.0 and DMD 2.x Git
(cfb5842b49),
which had slightly different behaviour; more of the sin tests
which should
pass in my opinion did with those two. Hence, it appears to be
regressions.
Post by Steven Schveighoffer via Digitalmars-d-learn
-Steve
Steven Schveighoffer via Digitalmars-d-learn
2014-10-24 12:38:53 UTC
Permalink
Post by Steven Schveighoffer via Digitalmars-d-learn
Post by Steven Schveighoffer via Digitalmars-d-learn
Using equality is not a good idea with floating point.
The compiler will on a whim, or depending on whether it can inline or
not, use higher precision floats, changing the outcome slightly.
I cannot say for certain whether this explains all the issues you
have, the very last one seems troubling to me at least.
Sure, in many cases it's a bad idea. While I understand that sin(PI) !=
0.0, but approxEqual(sin(PI), 0.0) == true, I would expect the following
assert (0.0 == 0.0);
assert (1.2345 == 1.2345);
F a = 1.2345, b = 9.8765;
assert (a+b == b+a);
assert (a*b == b*a);
None of these fail on my system
Same for me, that's the point; it's perfectly valid to compare floating
points.
OK. I think to be frank, your explanation by example needs to be
accompanied with what the result is (you do this below, thanks). From
the above, it sounds like they did not pass, but you expected them to.

As to your assertion that it's perfectly valid to compare floating
points, I think examples where they compare equal do not prove all
equivalent floating point calculations should compare equal.

Is it valid? Yes. Is it wise? No.
Post by Steven Schveighoffer via Digitalmars-d-learn
F fun (F a) pure;
assert (fun(a) + fun(b) == fun(b) + fun(a));
assert (fun(a) * fun(b) == fun(b) * fun(a));
auto a = fun(100);
auto b = fun(100);
assert (a == b);
assert (fun(100) == fun(100));
Not sure what body of fun is, so I cannot test this.
Could be anything taking a floating point and returning a floating point.
You would expect to get the same back for the same input, especially when
it's pure. If so, the asserts above are expected to hold.
OK, I get that. I agree, it should be equal.
Post by Steven Schveighoffer via Digitalmars-d-learn
auto c = fun(100);
auto d = fun(100);
assert (c == d); // Ok
assert (fun(100) != fun(100)) // I have a hard time understanding
// this is correct behaviour
Tried that out, it does not fail on my machine. Can you be more
specific on your testing? What compiler/platform? Stock compiler, or
did you build it yourself?
Right. It doesn't fail, and that's the problem.
Sorry, I said this wrong. I tried out fun(100) == fun(100) and the
assert passed.
assert (fun(100) == fun(100)); // Should pass, and does with
// body { return a + 1; }
// but not with
// body { return sin(a); }
Does not fail for me for sin(a) case.
Compiler: DMD32 D Compiler v2.066.0
OK, I tried with OSX 64-bit compiler. Perhaps 32 bit would not fare as
well. What platform are you testing on?

-Steve
deed via Digitalmars-d-learn
2014-10-24 12:49:25 UTC
Permalink
Post by Steven Schveighoffer via Digitalmars-d-learn
OK, I tried with OSX 64-bit compiler. Perhaps 32 bit would not
fare as well. What platform are you testing on?
Have tried Linux and Windows 64-bit and it seems to be an issue
when compiled with -m32. Tests are provided here
http://dpaste.dzfl.pl/5f55f4152aa8.

I agree that one cannot compare double and real and expect
equality, so the ones marked with [1] are not incorrect
behaviour, while [2] and [4] seem to be bugs.

H. S. Teoh via Digitalmars-d-learn
2014-10-23 18:51:09 UTC
Permalink
[...]
Post by Steven Schveighoffer via Digitalmars-d-learn
Post by deed via Digitalmars-d-learn
auto c = fun(100);
auto d = fun(100);
assert (c == d); // Ok
assert (fun(100) != fun(100)) // I have a hard time understanding
// this is correct behaviour
Tried that out, it does not fail on my machine. Can you be more
specific on your testing? What compiler/platform? Stock compiler, or
did you build it yourself?
[...]

A similar problem was recently (about 2-3 weeks ago IIRC) seen in one of
the Phobos PR's. It appears to be related to the autoextension of float
to double (or double to real, I forget which) in certain contexts on
Windows. @deed Could you please try to reduce the failing test to a
minimal code example, and post a disassembly of the concerned
function(s)? This could either be a subtle codegen bug, or something
more fundamentally broken with 80-bit real support.


T
--
Making non-nullable pointers is just plugging one hole in a cheese grater. -- Walter Bright
deed via Digitalmars-d-learn
2014-10-23 21:17:24 UTC
Permalink
Post by H. S. Teoh via Digitalmars-d-learn
A similar problem was recently (about 2-3 weeks ago IIRC) seen
in one of
the Phobos PR's. It appears to be related to the autoextension
of float
to double (or double to real, I forget which) in certain
contexts on
to a
minimal code example, and post a disassembly of the concerned
function(s)? This could either be a subtle codegen bug, or
something
more fundamentally broken with 80-bit real support.
T
Some testing can be found on http://dpaste.dzfl.pl/5f55f4152aa8
for both Windows and Linux. This just illustrates the sin
function.
deed via Digitalmars-d-learn
2014-10-23 21:28:33 UTC
Permalink
Post by deed via Digitalmars-d-learn
Some testing can be found on http://dpaste.dzfl.pl/5f55f4152aa8
for both Windows and Linux. This just illustrates the sin
function.
Replacing double with real makes everything pass on Linux Mint 16
with -m32 and -m64. Replacing double with float seems to give the
same problems as before, but hasn't been extensively tested. It
seems very likely to be a conversion issue, as H. S. Teoh
mentioned.
anonymous via Digitalmars-d-learn
2014-10-23 21:42:46 UTC
Permalink
Post by deed via Digitalmars-d-learn
Some testing can be found on http://dpaste.dzfl.pl/5f55f4152aa8
for both Windows and Linux. This just illustrates the sin
function.
I think the tests marked "[1]" are expected to fail. They involve
converting one operand of the comparison to double, but not the
other. The comparison is done using real precision. So, one
operand goes through a real->double->real conversion, which
changes the value.
anonymous via Digitalmars-d-learn
2014-10-23 22:07:21 UTC
Permalink
Post by anonymous via Digitalmars-d-learn
Post by deed via Digitalmars-d-learn
Some testing can be found on http://dpaste.dzfl.pl/5f55f4152aa8
for both Windows and Linux. This just illustrates the sin
function.
I think the tests marked "[1]" are expected to fail. They
involve
converting one operand of the comparison to double, but not the
other. The comparison is done using real precision. So, one
operand goes through a real->double->real conversion, which
changes the value.
Then again, I guess they're not guaranteed to fail. I remember
that the compiler is free to use higher than specified precision,
and that Walter feels strongly that this is the right thing to
do. So, the compiler could skip the conversion to double, making
the operands equal again.

And this is presumably what happens in the other failing tests,
where both operands are nominally doubles. One operand is indeed
converted to double. But the other is not, because the compiler
is free to do either.
deed via Digitalmars-d-learn
2014-10-24 07:48:41 UTC
Permalink
Post by anonymous via Digitalmars-d-learn
Post by deed via Digitalmars-d-learn
Some testing can be found on http://dpaste.dzfl.pl/5f55f4152aa8
for both Windows and Linux. This just illustrates the sin
function.
I think the tests marked "[1]" are expected to fail. They
involve
converting one operand of the comparison to double, but not the
other. The comparison is done using real precision. So, one
operand goes through a real->double->real conversion, which
changes the value.
You're right about those marked [1]; sin returns real and
shouldn't be expected to equal the same value truncated to double
or float and then extended back to real.

Converting the sin to double and compare is expected to work for
those, and it does when compiled with -m64, but not with -m32, on
Linux:

--
import std.math : sin;
import std.conv : to;
double fun (double a) { return sin(a); }

immutable double a = 3.
assert (fun(a) == sin(a).to!double); // Works when compiled with
-m64
--

The behaviour of those only marked [2] and [4] (the 32-bit
versions) seems to be a bug.
deed via Digitalmars-d-learn
2014-10-23 17:38:56 UTC
Permalink
Post by deed via Digitalmars-d-learn
assert (fasin(a) != fasin(a)); // ?
assert (facos(a) != facos(a)); // ?
Too quick there.. But:

assert (fasin(0.5) != fasin(0.5)); // ?
assert (facos(0.5) != facos(0.5)); // ?
Continue reading on narkive:
Loading...