www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Eliminate the baroque floating-point operators a la !<>=

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Hello,


I think the floating-point operators:

   a !<>= b
   a !<> b
   a <> b
   a <>= b
   a !> b
   a !>= b
   a !< b
   a !<= b

are useless. A simple peephole optimization in the compiler can 
automatically rewrite NaN test followed by regular operations into the 
operations above, for example:

isNaN(a) || isNan(b) || a >= b

is the same as

a !< b

This is in keeping with what the compiler does when seeing code like:

a = x / y;
b = x % y;

There's a peephole optimization that groups the / and the % together 
into an assembler operation that does both. If this is the way to go, we 
better be congruent and use explicit isNaN tests (that are then 
optimized) instead of defining eight extra operators.


Andrei
May 17 2009
next sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 17 May 2009 21:14:46 +0400, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:

 Hello,


 I think the floating-point operators:

    a !<>= b
    a !<> b
    a <> b
    a <>= b
    a !> b
    a !>= b
    a !< b
    a !<= b

 are useless. A simple peephole optimization in the compiler can  
 automatically rewrite NaN test followed by regular operations into the  
 operations above, for example:

 isNaN(a) || isNan(b) || a >= b

 is the same as

 a !< b

 This is in keeping with what the compiler does when seeing code like:

 a = x / y;
 b = x % y;

 There's a peephole optimization that groups the / and the % together  
 into an assembler operation that does both. If this is the way to go, we  
 better be congruent and use explicit isNaN tests (that are then  
 optimized) instead of defining eight extra operators.


 Andrei

Does anyone other than Don uses them at all? I don't care if they are removed from D.
May 17 2009
next sibling parent div0 <div0 users.sourceforge.net> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Denis Koroskin wrote:
 On Sun, 17 May 2009 21:14:46 +0400, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 
 Hello,


 I think the floating-point operators:

    a !<>= b
    a !<> b
    a <> b
    a <>= b
    a !> b
    a !>= b
    a !< b
    a !<= b

 are useless. A simple peephole optimization in the compiler can  
 automatically rewrite NaN test followed by regular operations into the  
 operations above, for example:

 isNaN(a) || isNan(b) || a >= b

 is the same as

 a !< b

 This is in keeping with what the compiler does when seeing code like:

 a = x / y;
 b = x % y;

 There's a peephole optimization that groups the / and the % together  
 into an assembler operation that does both. If this is the way to go, we  
 better be congruent and use explicit isNaN tests (that are then  
 optimized) instead of defining eight extra operators.


 Andrei

Does anyone other than Don uses them at all? I don't care if they are removed from D.

+vote Me too. isNaN is a lot easier to read. - -- My enormous talent is exceeded only by my outrageous laziness. http://www.ssTk.co.uk -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (MingW32) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFKEEiET9LetA9XoXwRAiyPAJ9CaZhm8eLuvzwmbsGtDiboEzz+qACfR9kD AvlFq15dYJO91X6vyLkvJMw= =J5dd -----END PGP SIGNATURE-----
May 17 2009
prev sibling parent reply Don <nospam nospam.com> writes:
Denis Koroskin wrote:
 On Sun, 17 May 2009 21:14:46 +0400, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 
 Hello,


 I think the floating-point operators:

    a !<>= b
    a !<> b
    a <> b
    a <>= b
    a !> b
    a !>= b
    a !< b
    a !<= b

 are useless. A simple peephole optimization in the compiler can  
 automatically rewrite NaN test followed by regular operations into the  
 operations above, for example:

 isNaN(a) || isNan(b) || a >= b

 is the same as

 a !< b

 This is in keeping with what the compiler does when seeing code like:

 a = x / y;
 b = x % y;

 There's a peephole optimization that groups the / and the % together  
 into an assembler operation that does both. If this is the way to go, we  
 better be congruent and use explicit isNaN tests (that are then  
 optimized) instead of defining eight extra operators.


 Andrei

Does anyone other than Don uses them at all? I don't care if they are removed from D.

Sad to say, I mostly only use !<>=, and that's because it's defined in the language so that it works at compile time (and is unaffected by the Tango/Phobos incompatibility problem). Also x!=x works just as well as an isNaN test. The silly thing is, in asm, I would never use the equivalent comparison. In x86, for instance, a floating point < compare sets TWO flags; one for less, one for NaN. You almost always want to treat NaN specially, so you do one compare and two branches. The NCEG operators only really allow you to move all of the special cases into a single test, but you end up doing a second comparison anyway, so it doesn't really buy you very much at all. Inclusion of the NCEG operators was a bit of tokenism, making a _very_ strong statement that D took numerical programmers seriously. But I think D's at the point where it can make that statement without relying on tokenism. Don.
May 17 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Don wrote:
 Inclusion of the NCEG operators was a bit of tokenism, making a _very_ 
 strong statement that D took numerical programmers seriously. But I 
 think D's at the point where it can make that statement without relying 
 on tokenism.

So is it ok to yank them? Andrei
May 17 2009
parent Don <nospam nospam.com> writes:
Andrei Alexandrescu wrote:
 Don wrote:
 Inclusion of the NCEG operators was a bit of tokenism, making a _very_ 
 strong statement that D took numerical programmers seriously. But I 
 think D's at the point where it can make that statement without 
 relying on tokenism.

So is it ok to yank them? Andrei

Yes if and only if we get agreement from Tango that tango2.math can use std.math. There's one other minor issue: isNaN() isn't supposed to trigger on signalling NaNs, but !<>= should trigger them. I'm not sure that _anyone_ actually cares about this difference (you can use isSignallingNaN() in the rare case where you don't want to trigger it), but it would be a difference from IEEE if we signalled when isNaN() is called, which is what would happen if we recreated the !<>= operators by merging isNaN with <,>, =.
May 18 2009
prev sibling next sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 Hello,
 I think the floating-point operators:
    a !<>= b
    a !<> b
    a <> b
    a <>= b
    a !> b
    a !>= b
    a !< b
    a !<= b
 are useless. A simple peephole optimization in the compiler can
 automatically rewrite NaN test followed by regular operations into the
 operations above, for example:
 isNaN(a) || isNan(b) || a >= b
 is the same as
 a !< b
 This is in keeping with what the compiler does when seeing code like:
 a = x / y;
 b = x % y;
 There's a peephole optimization that groups the / and the % together
 into an assembler operation that does both. If this is the way to go, we
 better be congruent and use explicit isNaN tests (that are then
 optimized) instead of defining eight extra operators.
 Andrei

I personally would like to see the whole NaNs not having a defined ordering thing done away with entirely. This probably won't happen b/c it's (I would guess) in the IEEE standard. However, the fact that a NaN is not less than, equal to, or greater than, anything creates an extremely annoying special case when doing generic programming for anything that requires a total ordering, such as trees and sorting. Sorting, trees, etc. require a consistent total ordering, and NaNs mean that, technically, floating point numbers don't have one. This means that any generic tree or sorting code that doesn't deal with this as a special case somehow is technically broken, or, if you look at it differently, these algorithms are invalid for floating point numbers. For example, try the following code in release mode (in debug mode, an assert fails). import std.stdio, std.algorithm, std.random; void main() { double[] foo = new double[20]; foreach(i, ref elem; foo) { elem = (i & 1) ? double.nan : i; } randomShuffle(foo); sort(foo); writeln(foo); } This is enough of a corner case that, in most of the generic code I write, I consider anything that expects a total ordering to just have completely undefined behavior when there isn't one, but this is obviously not an optimal solution. I actually prefer not to know what some of my code does when given a range of floats with a bunch of NaNs.
May 17 2009
parent reply Don <nospam nospam.com> writes:
dsimcha wrote:
 == Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 Hello,
 I think the floating-point operators:
    a !<>= b
    a !<> b
    a <> b
    a <>= b
    a !> b
    a !>= b
    a !< b
    a !<= b
 are useless. A simple peephole optimization in the compiler can
 automatically rewrite NaN test followed by regular operations into the
 operations above, for example:
 isNaN(a) || isNan(b) || a >= b
 is the same as
 a !< b
 This is in keeping with what the compiler does when seeing code like:
 a = x / y;
 b = x % y;
 There's a peephole optimization that groups the / and the % together
 into an assembler operation that does both. If this is the way to go, we
 better be congruent and use explicit isNaN tests (that are then
 optimized) instead of defining eight extra operators.
 Andrei

I personally would like to see the whole NaNs not having a defined ordering thing done away with entirely. This probably won't happen b/c it's (I would guess) in the IEEE standard. However, the fact that a NaN is not less than, equal to, or greater than, anything creates an extremely annoying special case when doing generic programming for anything that requires a total ordering, such as trees and sorting.

Agreed. It's quite ridiculous that x==x fails for some values of x (namely NaN). On this NG, I've mentioned the rationale for why that is (eg, if NaN==NaN, then sqrt(-4) == sqrt(-9)). But unfortunately, in solving an obscure corner case, the IEEE standard destroyed one of the most basic axioms in mathematics. It's one of those things that Seemed Like A Good Idea At The Time. The cure is a millions times worse than the disease. But it's in hardware, so it's Too Late Now.
May 17 2009
next sibling parent BCS <none anon.com> writes:
Hello Don,

 But unfortunately, in
 solving an obscure corner case, the IEEE standard destroyed one of the
 most basic axioms in mathematics.

But math deals with numbers (for the most part) and as the name says NaN is Not a Numebr. (Not that I think it is or isn't a bad idea, I'm not in a position to know.)
May 17 2009
prev sibling next sibling parent grauzone <none example.net> writes:
By the way, why is it that the is operator has the same behavior as == 
regarding NaNs? IMHO is should just compare the values bitwise.
May 17 2009
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Don wrote:
 dsimcha wrote:
 == Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s 
 article
 Hello,
 I think the floating-point operators:
    a !<>= b
    a !<> b
    a <> b
    a <>= b
    a !> b
    a !>= b
    a !< b
    a !<= b
 are useless. A simple peephole optimization in the compiler can
 automatically rewrite NaN test followed by regular operations into the
 operations above, for example:
 isNaN(a) || isNan(b) || a >= b
 is the same as
 a !< b
 This is in keeping with what the compiler does when seeing code like:
 a = x / y;
 b = x % y;
 There's a peephole optimization that groups the / and the % together
 into an assembler operation that does both. If this is the way to go, we
 better be congruent and use explicit isNaN tests (that are then
 optimized) instead of defining eight extra operators.
 Andrei

I personally would like to see the whole NaNs not having a defined ordering thing done away with entirely. This probably won't happen b/c it's (I would guess) in the IEEE standard. However, the fact that a NaN is not less than, equal to, or greater than, anything creates an extremely annoying special case when doing generic programming for anything that requires a total ordering, such as trees and sorting.

Agreed. It's quite ridiculous that x==x fails for some values of x (namely NaN). On this NG, I've mentioned the rationale for why that is (eg, if NaN==NaN, then sqrt(-4) == sqrt(-9)). But unfortunately, in solving an obscure corner case, the IEEE standard destroyed one of the most basic axioms in mathematics. It's one of those things that Seemed Like A Good Idea At The Time. The cure is a millions times worse than the disease. But it's in hardware, so it's Too Late Now.

Yah, there was a discussion on the C++ standardization mailing list too. It turns out that x == x is a pretty fundamental concept and if a type as common as float breaks it with panache, then it's pretty complicated to change language fundaments to account for such a case. Andrei
May 17 2009
prev sibling next sibling parent Jason House <jason.james.house gmail.com> writes:
Andrei Alexandrescu Wrote:

 Hello,
 
 
 I think the floating-point operators:
 
    a !<>= b
    a !<> b
    a <> b
    a <>= b
    a !> b
    a !>= b
    a !< b
    a !<= b
 
 are useless. A simple peephole optimization in the compiler can 
 automatically rewrite NaN test followed by regular operations into the 
 operations above, for example:
 
 isNaN(a) || isNan(b) || a >= b
 
 is the same as
 
 a !< b
 
 This is in keeping with what the compiler does when seeing code like:
 
 a = x / y;
 b = x % y;
 
 There's a peephole optimization that groups the / and the % together 
 into an assembler operation that does both. If this is the way to go, we 
 better be congruent and use explicit isNaN tests (that are then 
 optimized) instead of defining eight extra operators.
 
 
 Andrei

I like the concept of NaN-aware operators, but IMHO, the most common operators are prone to misuse. If D moves for the peephole optimizations, I'd request a function called ordered(a,b) in addition to isNaN(a). Non-nullable and non-nanable types would be handy...
May 17 2009
prev sibling next sibling parent reply Michiel Helvensteijn <m.helvensteijn.remove gmail.com> writes:
Andrei Alexandrescu wrote:

 I think the floating-point operators:
 
    a !<>= b
    a !<> b
    a <> b
    a <>= b
    a !> b
    a !>= b
    a !< b
    a !<= b
 
 are useless.

Actually, I like the concept of comparison operators for partial orders. When I first read the D1 specs, this set of operators was one of the features I especially liked. However, at the time I didn't realise they were solely for floating point numbers. They would make more sense if they could be overloaded. For example, they can be used for multi-objective optimization. With more than one objective, you need a way to express comparability of two potential solutions (<>= means comparable). They would be good for expressing the subset relation, where < is the strict subset relation and A <>= B would express that one of A and B is a subset of the other. Logically, they would be defined for the Boolean truth-values. False and true are incomparable to each other, but equal to themselves. And for those people that don't (want to) know about all of these extra operators, they don't have to. What's more, when it comes to integers, <> and != are equivalent, which is nice for those programmers that are familiar with one, but not the other. There's this thing, though. I would make it so !(a op b) is equivalent to (a !op b). It's a nice property. The IEEE standard requires that you set a certain exception flag when comparing with a NaN value, right? Well, just set the flag with the extra operators as well. The !(a op b) = (a !op b) thing can even be extended to the and/or/xor operators. I know that most of these operators don't make sense with integers and booleans. But you can give a compiler warning when you have a comparison with a predictable return value. In fact, everything I've said here is part of the Mist programming language. Inspired by D's example. :-) -- Michiel Helvensteijn
May 17 2009
parent Michiel Helvensteijn <m.helvensteijn.remove gmail.com> writes:
Michiel Helvensteijn wrote:

 There's this thing, though. I would make it so !(a op b) is equivalent to
 (a !op b). It's a nice property. The IEEE standard requires that you set a
 certain exception flag when comparing with a NaN value, right? Well, just
 set the flag with the extra operators as well. The !(a op b) = (a !op b)
 thing can even be extended to the and/or/xor operators.

Hm, I now realise this may be a problem for D, since it uses == for equality and != for non-equality. A consequence of using = for assignment, one of the most irritating backwards-C-compatibility things ever. -- Michiel Helvensteijn
May 17 2009
prev sibling parent Lionello Lunesu <lio lunesu.remove.com> writes:
   a <> b

It's an old wish of mine that this be used for the invocation of opCmp (instead of opEquals), returning an int, and valid for struct/class/built-ins/etc... (like perl's <=>, I think) As for the other operators, I never used them and I don't think I would use them ever often enough to remember what operator had what function. Using isNan sure is more clear and therefor safer :) As per Don's comment, the compiler would just have to remember that after a comparison the NaN flag is still correctly set and any isNan() could simply be skipped, resulting in only an additional branch instruction! L.
May 18 2009