Disallow (dis)equality with FP.nan/FP.init literals

bearophile bearophileHUGS at lycos.com
Wed Apr 18 17:50:08 PDT 2012


This is an open enhancement request that I'll probably add to 
Bugzilla.
The direct cause of it is a recent discussion in D.learn, but I 
and other people are aware of this problem for a lot of time.


Since a lot of time D statically refuses the use of 
"classReference == null":

// Program #1
class Foo {}
void main() {
     Foo f;
     assert(f == null);
     assert(f != null);
}


test.d(4): Error: use 'is' instead of '==' when comparing with 
null
test.d(5): Error: use '!is' instead of '!=' when comparing with 
null



A not expert D programmer sometimes compares a double with 
double.nan in a wrong way:

http://forum.dlang.org/thread/mailman.1845.1334694574.4860.digitalmars-d-learn@puremagic.com#post-jmlhfr:2428cv:241:40digitalmars.com

because someDouble == double.nan is always false:


// Program #2
import std.math: isNaN;
void main() {
     double x = double.init;
     assert(x != double.nan);
     assert(x != double.init);
     assert(isNaN(x));
     assert(x is double.init);
     assert(x !is double.nan);

     double y = double.nan;
     assert(y != double.nan);
     assert(y != double.init);
     assert(isNaN(y));
     assert(y !is double.init);
     assert(y is double.nan);
}



I think there are three common wrong usage patterns of NaNs 
testing:
1) Test that x is equal to/different from nan using 
x==FP.nan/x!=FP.nan
2) Test that x is equal to/different from all NaNs using 
x==FP.nan/x!=FP.nan
3) Test that x is equal to/different from FP.init using 
x==FP.init/x!=FP.init

The case 3 is a bit less important because the programmer already 
knows something about FP init, but it's wrong still.

There are other wrong usages of NaNs but they are by more expert 
programmers, to I don't want to catch them (example: using "is" 
to test if x is equal to/different from all NaNs is a bug, 
because there are more than one NaN. But if the programmer uses 
"is" I assume he/she/shi knows enough about NaNs, so this is not 
flagged by the compiler).

Currently this program compiles with no errors:


// Program #3
void main() {
     float x1 = float.nan;
     assert(x1 == float.nan);
     float x2 = 0.0;
     assert(x2 != float.nan);
     float x3 = float.init;
     assert(x3 == float.init);
     float x4 = 0.0;
     assert(x4 != float.init);

     double x5 = double.nan;
     assert(x5 == double.nan);
     double x6 = 0.0;
     assert(x6 != double.nan);
     double x7 = double.init;
     assert(x7 == double.init);
     double x8 = 0.0;
     assert(x8 != double.init);

     real x9 = real.nan;
     assert(x9 == real.nan);
     real x10 = 0.0;
     assert(x10 != real.nan);
     real x11 = real.init;
     assert(x11 == real.init);
     real x12 = 0.0;
     assert(x12 != real.init);

     enum double myNaN = double.nan;
     assert(myNaN == double.nan);
}



So I propose to statically disallow comparisons of Program #3, so 
it generates errors similar to:

test.d(4): Error: comparison is always false. Use 
'std.math.isNaN' to test for every kind of NaN or 'is float.nan' 
for this specific NaN
test.d(6): Error: comparison is always true. Use 
'!std.math.isNaN' to test for every kind of NaN or '!is 
float.nan' for this specific NaN
test.d(8): Error: comparison is always false. Use 
'std.math.isNaN' to test for every kind of NaN or 'is float.init' 
for this specicif NaN
...


Opinions, improvements, votes or critics are welcome.

Bye,
bearophile


More information about the Digitalmars-d mailing list