-
-
Notifications
You must be signed in to change notification settings - Fork 706
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Issue 13663 - Comparison of Tuples with floating point fields #7301
Conversation
|
Thanks for your pull request and interest in making D better, @berni44! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please see CONTRIBUTING.md for more information. If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment. Bugzilla references
Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub fetch digger
dub run digger -- build "master + phobos#7301" |
|
Two of the tests in buildkite produce this error: std/typecons.d(814,32): Error: template instance It's clear, that this comes from the tuple having named elements. But it seems like I need a more thorough understanding of how Tuple works for getting this right. |
|
I switched to math:isNaN, because now we know, that we compare floats. And: It feels a little bit strange to me, that the two functions differ only by a "const"... |
|
I just noticed, that the docs of opCmp where wrong too and obviously did not account for NaNs. I fixed that too. |
| if (isNaN(rhs.field[i])) | ||
| return float.nan; | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest adding this for user-defined types with float opCmps:
static if (isFloatingPoint!(typeof(field[i].opCmp(field[i]))))
{
if (field[i] != field[i])
return float.nan;
}
static if (isFloatingPoint!(typeof(rhs.field[i].opCmp(rhs.field[i]))))
{
if (rhs.field[i] != rhs.field[i])
return float.nan;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bah. The above fails since of course float doesn't have an opCmp member. Something like this is required:
static if (is(typeof({static assert(isFloatingPoint!(typeof(field[i].opCmp(field[i]))));})))
For the record: that's ugly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about that:
auto tmp1 = field[i] < rhs.field[i];
auto tmp2 = field[i] > rhs.field[i];
if (!tmp1 && !tmp2)
return float.nan;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That works, but brings us back to the performance discussion from earlier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, kinda obsoletes the isFloatingPoint test above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. So we need something else here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks better. One very minor nitpick is it's possible to have opCmp return a type with an overloaded opCmp that returns float (and actually any number of levels to this opCmp tower). I'm not sure why anyone would do that, but as a serial offender on operator overload abuse, I feel it's my duty to inform people of its dangers. Should we want to actually support this, I would suggest adding this to std.traits:
template hasNanComparison(T1, T2 = T1)
{
static if (isFloatingPoint!T1)
enum hasNanComparison = true;
else static if (!isAggregateType!T1)
enum hasNanComparison = false;
else static if (!is(typeof((T1 a, T2 b) => a.opCmp(b))))
enum hasNanComparison = false;
else
enum hasNanComparison = hasNanComparison!(ReturnType!((T1 a, T2 b) => a.opCmp(b)));
}
unittest {
assert( hasNanComparison!float);
assert( hasNanComparison!real);
assert(!hasNanComparison!int);
assert(!hasNanComparison!string);
assert(!hasNanComparison!(int*));
assert(!hasNanComparison!(int[]));
struct S
{
float opCmp(S s) { return float.nan; }
}
assert( hasNanComparison!S);
struct S2
{
S opCmp(S2 s) { return S(); }
}
assert( hasNanComparison!S2);
}
I would argue, however, that this addition is not necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this could be done in a separate PR if anyone needs it?!? I would be happy to finish with this PR, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
Next try. ;-) |
|
Closing this in favor of: #7748 |
Thanks to Simen Kjaeraas for pointing out this solution.