with the str thing, I can check for NAN but also easy the same way for +INF or -INF ...
with the str thing, I can check for NAN but also easy the same way for +INF or -INF ...
@Robert W I think you're confusing 'complicated' with 'reliable.'
Don't be upset. There is never one unique way of doing things. If people don't adopt your method, it is not the end of the world.
@Robert W I don't understand obsession of doing a string conversion. Why make it more complicated than necessary?
I just ran a comparison between my IsNaN function, and Christian's suggestion of using "x<>x". Here are the results:
Invalid Operation x*0<>0 x<>x log(0) True False log(-1) True True sqrt(-1) True True 1/0 True False -1/0 True False exp(1e40) True False Valid Operation x*0<>0 x<>x 100 False False 0/5 False False 2+4 False FalseThe x<>x comparison fails in many cases, apparently because it doesn't detect +inf or -inf
No.
Let's just look at passing 1/0 to your function (just follow it in the debugger):
1/0 is INF.
INF * 0 is NaN.
NaN is <> 0 so you return TRUE for the IsNaN function.
But the argument passed to your IsNaN function was INF, not NaN, so you return the wrong value.
Christian's function works (even if you pass 1/0 aka INF or -1/0 aka -INF to it):
Public Function IsNaN(x As Double) as Boolean return x<>x End Function
Yes, I pointed that out in my earlier post; it returns true for NaN, inf and -inf, which is what I find most useful for my needs. If you need to distinguish between these three results, then Christian's suggestion of using the x<>x test will return true only for NaN's.
I also need to post a warning about using these functions. Michel was correct about reliability. I was testing these again today, and discovered that both my function And Christian's function fail in 64 bit builds. I came up with a workaround for my function, but haven't yet attempted a workaround for Christian's.
Public Function IsNaN1(x As Double) as Boolean #If Target32Bit return (x*0<>0) #Else Return not(x*0<>0 or x*0=0) #Endif End Function
But please note that this has only been tested on Macintosh builds. So, if you wish to use it, then it should be tested on whichever hardware it's to be deployed on.
Because of this inconsistency, I decided to look at other ways of testing for NaN's. The most direct is to look at the bit pattern of the result of the mathematical operation. So, I came up with this alternative function:
Public Function IsNaN2(x As Double) as Boolean 'Test for invalid number: NaN, +inf, -inf static m As new MemoryBlock(8) m.DoubleValue(0) = x Return Bitwise.BitOr(m.Int64Value(0),&h800fffffffffffff)=-1 End Function
This can easily be modified to distinguish between NaN, inf and -inf.
For comparison purposes, I also put together a string based function:
Public Function IsNaN3(x As Double) as Boolean return InStr(str(x),"N")>0 End Function
Speed testing these three functions by running each one 10 million times, I get the following results:
Function Time (32 bit) Time (64 bit) -------- ------------- ------------- IsNan1 2.773251 sec. 0.5279456 sec. IsNan2 2.395691 sec. 1.862914 sec. IsNan3 12.01183 sec. 9.931013 sec.
@Robert W Yes, I pointed that out in my earlier post; it returns true for NaN, inf and -inf, which is what I find most useful for my needs.
You should not call your function IsNaN then … the name should always clearly reflect what it does.
@Robert W Christian's function fail in 64 bit builds
isn't it a xojo bug instead of a christian bad method ?
@Jean-YvesPochez isn't it a xojo bug instead of a christian bad method ?
Depends. Christian's method relies on NaN not comparing to itself. I don't think that is documented anywhere.
In my opinion, it's a Xojo bug.
The IEEE 754 specification which defines the standard double floating point format, and which defines NaN's, also specifies how NaN's should behave with comparison operators. Assuming that Xojo follows the IEEE 754 specification, then there must be a bug in either the 32 bit build or the 64 bit build, because the 32 and 64 bit builds should be consistent if they conform to IEEE 754.
Hey guys, I really really would prefer not to be a part of this thread because it's beyond my understanding, but I do need to know where things are at with this issue.
I've read this post a few times but there is no clear answer, (except maybe for Robert Weavers information in the preceding post.) There seems to be a lot of ideas thrown around, but no clear cut solution. Although Kems solution was looking good, then is seemed to be disregarded because of an apparent flaw.
I'm certainly not offering a solution. I just need an answer.
I've had the issue before (about a year ago) and it wasn't a big deal because it was a "display" issue and never affected any important calculations. I got around it something like this:
// detect if nan or Inf If InStr (showCurrentGs,"Inf") <> 0 Then showCurrentGs = "0.0" End If If InStr (showCurrentGs,"nan")<> 0 Then showCurrentGs = "0.0" End If //
However, now I have a situation where a number is calculated and sent to an external device. I believe I'm getting (-Nan) and the external device becomes corrupt. Although it can be reset (via software), this is not an acceptable scenario.
I haven't done any more work on my software for the last week because I this issue. I've done some reading but want to make sure the solution is as foolproof as possible.
I guess I could try InStr as before - or is Kems solution better?
The Regex solution appears to be more intended to check the validity of user input. If your variable showCurrentGs is the direct output of a numeric calculation, then it will either be a valid number, or it will be a NaN, or inf. So your test will work fine as is. In fact you can simplify it to a single test to look for the character "n" which would never appear in a normal number.
// detect if nan or Inf If InStr (showCurrentGs,"n") <> 0 Then showCurrentGs = "0.0" End If
This is essentially the same code as the IsNan3 function that I posted above.
Thanks Robert.
It turns out that there were two places that the error was occurring which made it hard to track down.
The main issue was displaying the calculated result from a double (calAverage). So I've used your simplified method:
calAverage = round((total / (calibrationArray.Ubound-1) - calibrationPointNEW)) // Displayed Calibrated/Tare If InStr (Str(calAverage),"n") <> 0 Then calAverage = 0 End If lblShowCalibratedData.Text = Str(Format(calAverage/1000, "-00.000")) //Display formatted result
I haven't seen (-Nan) since. The poor dear... :)
Also, I think it was just was I was "seeing" and the (-Nan) string was never sent to the external device like that, or if it was, it didn't cause an issue. It was just internal calculations that became corrupt. I tried to check my external serial device via the Arduino IDE to check the data, but starting that IDE resets the device and therefore I could never tell.
All looking good now though. Thanks Robert.
Cheers.
This is such an interesting yet infuriating problem. I'm porting a physics library from C# and so came across a need for a function to return True
if a number is a NaN
but False
if it's a real number or positive/negative infinity.
Using String
or Text
comparisons is unacceptable as the library makes thousands of these checks and I can't tolerate the performance overhead.
I've been trying out its of the solutions above. I think there must be a bug in Xojo's comparator operator for Doubles
.
macOS 64-bit
Public Function IsNan1(x As Double) Return x <> x End Function Dim r1 As Boolean = IsNan1(10) // Real number Dim nan1 As Boolean = IsNan1(Sqrt(-1)) // NaN Dim nan1b As Boolean = IsNan2(0/0) // NaN Dim log01 As Boolean = IsNan1(Log(0)) // - Infinity Dim negInf1 As Boolean = IsNan1(-1/0) // - Infinity Dim inf1 As Boolean = IsNan1(1/0) // Infinity Dim exp1e41 As Boolean = IsNaN1(Exp(1e40)) // Infinity
Results:
r1 = False (correct)
nan1 = False (wrong)
nan1b = False (wrong)
log01 = False (correct)
negInf1 = False (correct)
inf1 = False (correct)
exp1e401 = False (correct)
Public Function IsNan2(x As Double) Return Not (x = x) End Function Dim r2 As Boolean = IsNan2(10) // Real number Dim nan2 As Boolean = IsNan2(Sqrt(-1)) // NaN Dim nan2b As Boolean = IsNan2(0/0) // NaN Dim log02 As Boolean = IsNan2(Log(0)) // - Infinity Dim negInf2 As Boolean = IsNan2(-1/0) // - Infinity Dim inf2 As Boolean = IsNan2(1/0) // Infinity Dim exp1e402 As Boolean = IsNaN2(Exp(1e40)) // Infinity
Results:
r2 = False (correct)
nan2 = True (correct)
nan2b = True (correct)
log02 = False (correct)
negInf2 = False (correct)
inf2 = False (correct)
exp1e402 = False (correct)
As you can see, IsNan2
works correctly, IsNan1
fails. What is strange is x <> x
should give the same result as Not (x = x)
but it doesn't. Surely this is a bug?
Looks like it may be a bug in Xojo...
I duplicated your exact test in SWIFT and it produced the results you seemed to have been expecting
func isNAN(_ x:Double) -> Bool { return (x != x) } func test() { print("---- START TEST ----") print( isNAN(10) ) print( isNAN(sqrt(-1) ) ) print( isNAN(0/0) ) print( isNAN(log(0)) ) print( isNAN(-1/0) ) print( isNAN(1/0) ) print( isNAN(exp(1e40)) ) print("---- END TEST ----") }
---- START TEST ----
false
true
true
false
false
false
false
---- END TEST ----
if produced the SAME results if I changed the isNAN function the same as you had
func isNAN(_ x:Double) -> Bool { return !(x == x) }
I deliberately made my versions of IsNaN functions to return true for NaN +Inf and -Inf. The memoryblock based function is probably the fastest and most reliable. It can be modified to return true only for NaNs and false for +/-Inf, since the function is looking directly at the NaN bits in the number. Here is a modified function which I put together rather quickly and haven't had a chance to test yet.
Public Function IsNaN3(x As Double) as Boolean 'Test for invalid number: NaN, but not +inf, -inf static m As new MemoryBlock(8) m.DoubleValue(0) = x Return (Bitwise.BitOr(m.Int64Value(0),&h800fffffffffffff)=-1)_ and (Bitwise.BitAnd(m.Int64Value(0),&h000fffffffffffff)<>0) End Function
@Garry P This is such an interesting yet infuriating problem... I think there must be a bug in Xojo's comparator operator for Doubles.
Just recently I've noticed that when it comes to trying to "compare" NaN, the Xojo Framework behaves differently on Target32Bit and Target64Bit. And also some differences between Platforms...
I can't say if that's related to your situation - but it's maybe worth being aware of how Xojo handles this currently...:
Feedback Case #55370 NaN: Different behavior and comparison results in Target32Bit and Target64Bit
I've now had a chance to test the IsNaN3 function I gave in my last post, and it appears to work correctly:
log(-1): True ASin(500): True sqrt(-5): True log(0): False (Inf but not NaN) 1/0: False (-1)/0: False
The method of putting the double being tested into a memory block should be reliable, because we can then directly test the bits that determine whether the number is a NaN, or an Inf, and not have to guess at the internal logic of Xojo's comparison operators.
The format of a double is
SEEEEEEE EEEEFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
where S=sign bit, E=exponent bits, F=fraction bits (also called mantissa or significand)
If all 11 bits of the exponent are set to 1, it indicates an abnormal value: either a NaN or an INF. My original function simply checked these bits to see if they were all 1's, because for my purposes I didn't need to distinguish between NaN's and Inf's. However, to distinguish between a NaN and Inf it's necessary to check the fraction bits as well. If they are all 0's then it's an Inf. (In the case of an Inf, the sign bit determines whether it's a +Inf or a -Inf.) Otherwise it's a NaN. So, the second bitwise test checks the fraction bits for all 0's.
I agree that testing the bits is the best approach. You can optimize that by using the AND and OR operators instead of the Bitwise functions (no function call overhead).
I thought AND and OR were just shorthand, and still called the bitwise functions.
No, they compile direct to machine code. Much faster, and the compiler can further optimize if possible.
Thanks. In that case, here is the simpified code using the AND and OR operators:
Function IsNaN3(x As Double) as Boolean 'Test for invalid number: NaN, but not +inf, -inf 'using bitwise ops to detect the actual IEEE-754 NaN bit pattern static m As new MemoryBlock(8) m.DoubleValue(0) = x Return ((m.Int64Value(0) OR &h800fffffffffffff)=-1) AND ((m.Int64Value(0) AND &h000fffffffffffff)<>0) End Function