Problems with NaN (not a number)

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:

[code] 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[/code]

I haven’t seen (-Nan) since. The poor dear… :slight_smile:

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

[code]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[/code]

Results:
r1 = False (correct)
nan1 = False (wrong)
nan1b = False (wrong)
log01 = False (correct)
negInf1 = False (correct)
inf1 = False (correct)
exp1e401 = False (correct)

[code]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[/code]

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

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…:
<https://xojo.com/issue/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