Skip to content
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

Closed
wants to merge 1 commit into from
Closed

Fix Issue 13663 - Comparison of Tuples with floating point fields #7301

wants to merge 1 commit into from

Conversation

ghost
Copy link

@ghost ghost commented Dec 4, 2019

Thanks to Simen Kjaeraas for pointing out this solution.

@ghost ghost requested a review from MetaLang as a code owner December 4, 2019 11:54
@dlang-bot
Copy link
Contributor

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 verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the coverage diff by visiting the details link of the codecov check)
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

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

Auto-close Bugzilla Severity Description
13663 normal Comparison of Tuples with floating point fields

Testing this PR locally

If 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"

std/typecons.d Outdated Show resolved Hide resolved
std/typecons.d Outdated Show resolved Hide resolved
std/typecons.d Outdated Show resolved Hide resolved
std/typecons.d Outdated Show resolved Hide resolved
@ghost
Copy link
Author

ghost commented Dec 4, 2019

Two of the tests in buildkite produce this error:

std/typecons.d(814,32): Error: template instance isFloatingPoint!"key" does not match template declaration isFloatingPoint(T)

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.

std/typecons.d Outdated Show resolved Hide resolved
@ghost
Copy link
Author

ghost commented Dec 5, 2019

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

@ghost
Copy link
Author

ghost commented Dec 5, 2019

I just noticed, that the docs of opCmp where wrong too and obviously did not account for NaNs. I fixed that too.

std/typecons.d Outdated Show resolved Hide resolved
if (isNaN(rhs.field[i]))
return float.nan;
}

Copy link
Contributor

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;
}

Copy link
Contributor

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.

Copy link
Author

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;

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Author

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

Copy link
Contributor

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.

Copy link
Author

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ghost
Copy link
Author

ghost commented Jan 3, 2020

Next try. ;-)

@RazvanN7
Copy link
Collaborator

Closing this in favor of: #7748

@RazvanN7 RazvanN7 closed this Jan 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants