Date: Tue, 26 Sep 2023 21:34:16 -0600
This is still completely branch-free for NaN.
From:
`
// user-defined, so it may or may not be horribly slow or return -50
int compare( nan_able a, nan_able b )
{
if( isnan(a) || isnan(b) )
{ return -2; }
// Otherwise equivalent to this:
return sgn_as_int( a - b );
}
Operator equivalents:
bool operator == ( nan_able a, nan_able b )
{
return compare( a, b ) == 0;
}
bool operator != ( nan_able a, nan_able b )
{
static const bool tbl_0[4]
{
false, // saturation of any other value to -2
true, // less -> a != b
false, // equal -> ! ( a != b )
true // greater -> a != b
};
static const bool *tbl = &tbl_0[2];
return tbl[ saturate_to_tbl( compare( a, b ) ) ];
}
bool operator < ( nan_able a, nan_able b )
{
return compare( a, b ) == -1;
}
bool operator <= ( nan_able a, nan_able b )
{
static const bool tbl_0[4]
{ false, true, true, false };
static const bool *tbl = &tbl_0[2];
return tbl[ saturate_to_tbl( compare( a, b ) ) ];
}
bool operator > ( nan_able a, nan_able b )
{
return compare( a, b ) > 0;
}
bool operator >= ( nan_able b, nan_able b )
{
static const bool tbl_0[4]
{ false, false, true, true };
static const bool *tbl = &tbl_0[2];
return tbl[ saturate_to_tbl( compare( a, b ) ) ];
}`
This handles the "NaN <op> NaN" == false convention with the worst case
overhead of a branch-free saturation and table lookup if more than one
possibility needs to be tested.
I was hoping another compiler author might step in and offer something
similar, then demonstrate why it is impossible to incorporate in some other
way I did not consider.
So, should it assume all value types are nan_able? Yes, since the proposed
definition of the operator with the int return type is to check for
containment in the set {-1,0,1}, which requires a saturate-then-lookup in
this case to be robust. I can imagine there might be some challenges for an
optimizer to overcome.
Why not in the opening statement? I use isnan() and hardware exceptions
(with hw floats) to catch the problems closer to where they happen. I
thought this was advised as good practice.
What happens when predicates start returning -2? I use something like an
"is_unspec" test for relationships applied wherever a ternary is used in
situations where that might be a problem.
Consequently, I don't consider oddball comparison behavior an adequate
error trapping mechanism. isnan is not defined as "!( a != a )", and I
don't expect the same from a hw-based comparison.
I fail to see why a strongly-typed ternary, mandatory for use with <=>,
provides an adequate solution to this problem. Especially where the
operator is user-defined.
All it does is create what I consider to be a nuisance of a dependency.
On Tue, Sep 26, 2023 at 7:40 PM Chris Gary <cgary512_at_[hidden]> wrote:
> Its hard for me to say what inspired many of the responses I received,
> aside from my clicking "reply to" and having a good bit of unproductive
> discussion vanish into personal conversations with others. Yet, here, it is
> not having problems putting your email on the CC list, and the message
> board on the main...
>
> We have a set of 3 things in std::strong_ordering, and an implicit 4th
> that is "not defined" or "unspecified". "Unspecified" is not an error, its
> just UB, which we've all come to know and work with. Proving that an
> arbitrary operator <=>, return type notwithstanding, will never result in
> UB is ultimately undecidable (also obvious).
>
> Wherever a user-defined operator <=> suddenly produces an int, the
> compiler can instead insert code to check against any of {-1,0,1} depending
> on what was synthesized. No need for a header or privileged symbols in
> namespace std. Again, I proposed this assuming a good deal of other things
> were implied.
>
> The comment about "Hacking into Clang..." was meant for another thread,
> the discussion in which seemed to creep into this one. At least a mention
> of "polyfill" was made in one post I responded to, and that ended up here.
>
> This won't break or interact with modules as far as I can tell. In general
> code, obviously something like decltype( a <=> b ), but then wherever that
> is being done, it probably doesn't care about specific names...
>
> To every complaint about "ABI breakage" etc... It won't break any existing
> code to allow new code to define an int-valued <=>. Its just a function by
> another name. The behavior of the compiler can be made well-defined in
> either case.
>
> The comment in the opening about "all knowledge being integers" etc...
> also obvious, was meant to set the tone to "this is always possible,
> reasoning to the contrary must now confront an obvious truth." Why? I feel
> the decision to impose semantics through a return type is bad design, and
> thought the reasoning for its adoption was due to a narrow scope of
> understanding. Started by yours truly, all bad ideas that just ended up
> gaslighting many very negative responses, then more confused negativity
> from myself, etc...
>
> I would need a specific example of where, if some new code were allowed to
> define an int-valued operator <=>, this would make it impossible for it to
> build against other code. I see this as analogous to "std::strong_ordering
> compare( thing a, thing b )" being in the same overload set as "int
> compare( different_thing a, different_thing b )". Type conversion issues
> aside; two functions, with the same name, different arguments and a
> different return type haven't been an issue before.
>
> There is too much to review from this mess right now. I felt an
> explanation was warranted from me. If an example has already been posted,
> I'll eventually find it.
>
> On Mon, Sep 25, 2023 at 2:47 PM Julien Villemure-Fréchette via
> Std-Proposals <std-proposals_at_[hidden]> wrote:
>
>> > It seems presenting ideas here without first providing a toy
>> implementation never gets any traction. "If you can't hack it into
>> Clang, then nobody cares."
>>
>> I certainly do not think that "nobody cares" here, just look at the
>> tremendous amount of replies got generated, I didn't count the exact
>> number, but I'm sure we're over 20;
>> and at least from 5 different people (which includes active committee
>> members). You actually did get a lot of attention, with a fair amount of
>> text in responses to your OP,
>> with detailed explanations as to why people reading this thread actually
>> judge that what you propose has not enough value to get moving forward.
>>
>> Even though you could be right in identifying an unsolved problem, and
>> if so, that your proposed solution is a good one, people are saying "I'm
>> not convinced".
>> (Think of special relativity, nobody took it seriously; it only was
>> accepted years later, when a rigorous experiment disproved the "Ether
>> model").
>> If you fail to convince others, or nobody seem to see the problem you
>> indicate as something worth fixing, then you must try to prove your point.
>> Rephrasing things, or replying by adding more sentences like "things
>> should be like this" (which already sound opinionated),
>> or personal anecdotes of the kind "something broke when I did this, but
>> shouldn't had" (which is hypothetical), then it's not going to be
>> perceived seriously,
>> and is no more convincing than repeating the same thing over and over.
>>
>> If you want things to go forward, you need to break this
>> "argument-counterargument" cycle, you need to rigorous proof that
>> 1) there really is an outstanding problem that needs to be fixed;
>> 2) and that your solution fixes this problem without bringing in
>> contradictions or breaking other people's (conforming) code;
>>
>> What I mean by a "proof" is, a tangible, reproductible experiment with
>> consistent observables: describe the steps of the experiment so that
>> anyone can
>> run it, and then come up with consistent observations leading to the
>> same conclusions.
>>
>> The sentence "If you can't hack it into Clang, then nobody cares."
>> sounds more like a catch phrase and can be taken to mean "prove your
>> point using a rigorous method".
>> If you someone wants to propose some change in the language's syntax,
>> starting a topic branch from clang is usually a convenient experimental
>> setup to support their proposal.
>> Other methods are not excluded.
>>
>> > ... and even in my personal projects I really do not care about that
>> kind of thing
>>
>> The terms "my personal projects" and "I do not care about that kind of
>> thing" are antithetical to collaborative work --
>> how about the reciprocal "We don't interact with your personal projects,
>> so we don't care about the problems you encounter or your personal
>> specific needs".
>> A problem is worth solving if it reaches more people than just you;
>> A solution has value if it improves the language for everyone, in way
>> consistent with the philosophy of the language.
>>
>> BTW, If you don't like that language mechanics to interact with std
>> library headers, C language has come with the "nice solution" of
>> introducing
>> new builtin types with new builtin operator semantics for complex
>> numbers and booleans. To do so, in a portable way (without breaking
>> backward compatibility),
>> type names needed to be in the _Reserved_identifier space, and more
>> special cases in the formal description of builtin operators (as C
>> cannot support builtin type's syntax for user defined types).
>> Does anyone have objection if I'd say `std::new_type` is better than
>> `_New_type` or that implementing an `operator+` directly in C++ is
>> better than adding a new special case to the builtin operator `+`
>> to support the semantic of some `_New_type`? If I recall, supporting
>> user defined types with syntax similar to builtin types is foundational
>> in C++.
>>
>> Julien V.
>>
>> On 2023-09-24 10:38 p.m., Chris Gary via Std-Proposals wrote:
>> > have to come back to these things later.
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>
>
From:
`
// user-defined, so it may or may not be horribly slow or return -50
int compare( nan_able a, nan_able b )
{
if( isnan(a) || isnan(b) )
{ return -2; }
// Otherwise equivalent to this:
return sgn_as_int( a - b );
}
Operator equivalents:
bool operator == ( nan_able a, nan_able b )
{
return compare( a, b ) == 0;
}
bool operator != ( nan_able a, nan_able b )
{
static const bool tbl_0[4]
{
false, // saturation of any other value to -2
true, // less -> a != b
false, // equal -> ! ( a != b )
true // greater -> a != b
};
static const bool *tbl = &tbl_0[2];
return tbl[ saturate_to_tbl( compare( a, b ) ) ];
}
bool operator < ( nan_able a, nan_able b )
{
return compare( a, b ) == -1;
}
bool operator <= ( nan_able a, nan_able b )
{
static const bool tbl_0[4]
{ false, true, true, false };
static const bool *tbl = &tbl_0[2];
return tbl[ saturate_to_tbl( compare( a, b ) ) ];
}
bool operator > ( nan_able a, nan_able b )
{
return compare( a, b ) > 0;
}
bool operator >= ( nan_able b, nan_able b )
{
static const bool tbl_0[4]
{ false, false, true, true };
static const bool *tbl = &tbl_0[2];
return tbl[ saturate_to_tbl( compare( a, b ) ) ];
}`
This handles the "NaN <op> NaN" == false convention with the worst case
overhead of a branch-free saturation and table lookup if more than one
possibility needs to be tested.
I was hoping another compiler author might step in and offer something
similar, then demonstrate why it is impossible to incorporate in some other
way I did not consider.
So, should it assume all value types are nan_able? Yes, since the proposed
definition of the operator with the int return type is to check for
containment in the set {-1,0,1}, which requires a saturate-then-lookup in
this case to be robust. I can imagine there might be some challenges for an
optimizer to overcome.
Why not in the opening statement? I use isnan() and hardware exceptions
(with hw floats) to catch the problems closer to where they happen. I
thought this was advised as good practice.
What happens when predicates start returning -2? I use something like an
"is_unspec" test for relationships applied wherever a ternary is used in
situations where that might be a problem.
Consequently, I don't consider oddball comparison behavior an adequate
error trapping mechanism. isnan is not defined as "!( a != a )", and I
don't expect the same from a hw-based comparison.
I fail to see why a strongly-typed ternary, mandatory for use with <=>,
provides an adequate solution to this problem. Especially where the
operator is user-defined.
All it does is create what I consider to be a nuisance of a dependency.
On Tue, Sep 26, 2023 at 7:40 PM Chris Gary <cgary512_at_[hidden]> wrote:
> Its hard for me to say what inspired many of the responses I received,
> aside from my clicking "reply to" and having a good bit of unproductive
> discussion vanish into personal conversations with others. Yet, here, it is
> not having problems putting your email on the CC list, and the message
> board on the main...
>
> We have a set of 3 things in std::strong_ordering, and an implicit 4th
> that is "not defined" or "unspecified". "Unspecified" is not an error, its
> just UB, which we've all come to know and work with. Proving that an
> arbitrary operator <=>, return type notwithstanding, will never result in
> UB is ultimately undecidable (also obvious).
>
> Wherever a user-defined operator <=> suddenly produces an int, the
> compiler can instead insert code to check against any of {-1,0,1} depending
> on what was synthesized. No need for a header or privileged symbols in
> namespace std. Again, I proposed this assuming a good deal of other things
> were implied.
>
> The comment about "Hacking into Clang..." was meant for another thread,
> the discussion in which seemed to creep into this one. At least a mention
> of "polyfill" was made in one post I responded to, and that ended up here.
>
> This won't break or interact with modules as far as I can tell. In general
> code, obviously something like decltype( a <=> b ), but then wherever that
> is being done, it probably doesn't care about specific names...
>
> To every complaint about "ABI breakage" etc... It won't break any existing
> code to allow new code to define an int-valued <=>. Its just a function by
> another name. The behavior of the compiler can be made well-defined in
> either case.
>
> The comment in the opening about "all knowledge being integers" etc...
> also obvious, was meant to set the tone to "this is always possible,
> reasoning to the contrary must now confront an obvious truth." Why? I feel
> the decision to impose semantics through a return type is bad design, and
> thought the reasoning for its adoption was due to a narrow scope of
> understanding. Started by yours truly, all bad ideas that just ended up
> gaslighting many very negative responses, then more confused negativity
> from myself, etc...
>
> I would need a specific example of where, if some new code were allowed to
> define an int-valued operator <=>, this would make it impossible for it to
> build against other code. I see this as analogous to "std::strong_ordering
> compare( thing a, thing b )" being in the same overload set as "int
> compare( different_thing a, different_thing b )". Type conversion issues
> aside; two functions, with the same name, different arguments and a
> different return type haven't been an issue before.
>
> There is too much to review from this mess right now. I felt an
> explanation was warranted from me. If an example has already been posted,
> I'll eventually find it.
>
> On Mon, Sep 25, 2023 at 2:47 PM Julien Villemure-Fréchette via
> Std-Proposals <std-proposals_at_[hidden]> wrote:
>
>> > It seems presenting ideas here without first providing a toy
>> implementation never gets any traction. "If you can't hack it into
>> Clang, then nobody cares."
>>
>> I certainly do not think that "nobody cares" here, just look at the
>> tremendous amount of replies got generated, I didn't count the exact
>> number, but I'm sure we're over 20;
>> and at least from 5 different people (which includes active committee
>> members). You actually did get a lot of attention, with a fair amount of
>> text in responses to your OP,
>> with detailed explanations as to why people reading this thread actually
>> judge that what you propose has not enough value to get moving forward.
>>
>> Even though you could be right in identifying an unsolved problem, and
>> if so, that your proposed solution is a good one, people are saying "I'm
>> not convinced".
>> (Think of special relativity, nobody took it seriously; it only was
>> accepted years later, when a rigorous experiment disproved the "Ether
>> model").
>> If you fail to convince others, or nobody seem to see the problem you
>> indicate as something worth fixing, then you must try to prove your point.
>> Rephrasing things, or replying by adding more sentences like "things
>> should be like this" (which already sound opinionated),
>> or personal anecdotes of the kind "something broke when I did this, but
>> shouldn't had" (which is hypothetical), then it's not going to be
>> perceived seriously,
>> and is no more convincing than repeating the same thing over and over.
>>
>> If you want things to go forward, you need to break this
>> "argument-counterargument" cycle, you need to rigorous proof that
>> 1) there really is an outstanding problem that needs to be fixed;
>> 2) and that your solution fixes this problem without bringing in
>> contradictions or breaking other people's (conforming) code;
>>
>> What I mean by a "proof" is, a tangible, reproductible experiment with
>> consistent observables: describe the steps of the experiment so that
>> anyone can
>> run it, and then come up with consistent observations leading to the
>> same conclusions.
>>
>> The sentence "If you can't hack it into Clang, then nobody cares."
>> sounds more like a catch phrase and can be taken to mean "prove your
>> point using a rigorous method".
>> If you someone wants to propose some change in the language's syntax,
>> starting a topic branch from clang is usually a convenient experimental
>> setup to support their proposal.
>> Other methods are not excluded.
>>
>> > ... and even in my personal projects I really do not care about that
>> kind of thing
>>
>> The terms "my personal projects" and "I do not care about that kind of
>> thing" are antithetical to collaborative work --
>> how about the reciprocal "We don't interact with your personal projects,
>> so we don't care about the problems you encounter or your personal
>> specific needs".
>> A problem is worth solving if it reaches more people than just you;
>> A solution has value if it improves the language for everyone, in way
>> consistent with the philosophy of the language.
>>
>> BTW, If you don't like that language mechanics to interact with std
>> library headers, C language has come with the "nice solution" of
>> introducing
>> new builtin types with new builtin operator semantics for complex
>> numbers and booleans. To do so, in a portable way (without breaking
>> backward compatibility),
>> type names needed to be in the _Reserved_identifier space, and more
>> special cases in the formal description of builtin operators (as C
>> cannot support builtin type's syntax for user defined types).
>> Does anyone have objection if I'd say `std::new_type` is better than
>> `_New_type` or that implementing an `operator+` directly in C++ is
>> better than adding a new special case to the builtin operator `+`
>> to support the semantic of some `_New_type`? If I recall, supporting
>> user defined types with syntax similar to builtin types is foundational
>> in C++.
>>
>> Julien V.
>>
>> On 2023-09-24 10:38 p.m., Chris Gary via Std-Proposals wrote:
>> > have to come back to these things later.
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>
>
Received on 2023-09-27 03:34:30