digitalmars.D.learn - Bug?
- deed (63/63) Oct 23 2014 // DMD v2.066.0
- deed (3/10) Oct 23 2014 // Fun, gun, pun...
- Steven Schveighoffer (7/9) Oct 23 2014 Using equality is not a good idea with floating point.
- deed (22/29) Oct 23 2014 Sure, in many cases it's a bad idea. While I understand that
- Steven Schveighoffer (7/36) Oct 23 2014 Not sure what body of fun is, so I cannot test this.
- deed (23/75) Oct 23 2014 Same for me, that's the point; it's perfectly valid to compare
- Steven Schveighoffer (15/74) Oct 24 2014 OK. I think to be frank, your explanation by example needs to be
- deed (6/8) Oct 24 2014 Have tried Linux and Windows 64-bit and it seems to be an issue
- H. S. Teoh via Digitalmars-d-learn (13/26) Oct 23 2014 [...]
- deed (3/5) Oct 23 2014 Too quick there.. But:
// 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
Oct 23 2014
-- 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
Oct 23 2014
On 10/23/14 1:08 PM, deed wrote:// 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
Oct 23 2014
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. -SteveSure, 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
Oct 23 2014
On 10/23/14 2:18 PM, deed wrote:None of these fail on my systemUsing 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 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));Not sure what body of fun is, so I cannot test this.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 behaviourTried 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
Oct 23 2014
On Thursday, 23 October 2014 at 18:26:53 UTC, Steven Schveighoffer wrote:On 10/23/14 2:18 PM, deed wrote:Same for me, that's the point; it's perfectly valid to compare floating points.None of these fail on my systemUsing 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 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);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.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.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.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 behaviourTried 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
Oct 23 2014
On 10/23/14 2:46 PM, deed wrote:On Thursday, 23 October 2014 at 18:26:53 UTC, Steven Schveighoffer wrote: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.On 10/23/14 2:18 PM, deed wrote:Same for me, that's the point; it's perfectly valid to compare floating points.None of these fail on my systemUsing 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 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);OK, I get that. I agree, it should be equal.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.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.Sorry, I said this wrong. I tried out fun(100) == fun(100) and the assert passed.Right. It doesn't fail, and that's the problem.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 behaviourTried 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?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.0OK, I tried with OSX 64-bit compiler. Perhaps 32 bit would not fare as well. What platform are you testing on? -Steve
Oct 24 2014
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.
Oct 24 2014
On Thu, Oct 23, 2014 at 02:26:52PM -0400, Steven Schveighoffer via Digitalmars-d-learn wrote:On 10/23/14 2:18 PM, deed wrote:[...][...] 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 BrightNow, 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 behaviourTried 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?
Oct 23 2014
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. TSome testing can be found on http://dpaste.dzfl.pl/5f55f4152aa8 for both Windows and Linux. This just illustrates the sin function.
Oct 23 2014
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.
Oct 23 2014
On Thursday, 23 October 2014 at 21:17:25 UTC, deed wrote: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.
Oct 23 2014
On Thursday, 23 October 2014 at 21:42:46 UTC, anonymous wrote:On Thursday, 23 October 2014 at 21:17:25 UTC, deed wrote: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.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.
Oct 23 2014
On Thursday, 23 October 2014 at 21:42:46 UTC, anonymous wrote:On Thursday, 23 October 2014 at 21:17:25 UTC, deed wrote: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.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.
Oct 24 2014
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)); // ?
Oct 23 2014