views:

131

answers:

3

I'm writing Python 2.6 code that interfaces with NI TestStand 4.2 via COM in Windows. I want to make a "NAN" value for a variable, but if I pass it float('nan'), TestStand displays it as IND.

Apparently TestStand distinguishes between floating point "IND" and "NAN" values. According to TestStand help:

  • IND corresponds to Signaling NaN in Visual C++, while
  • NAN corresponds to QuietNaN

That implies that Python's float('nan') is effectively a Signaling NaN when passed through COM. However, from what I've read about Signaling NaN, it seems that Signaling NaN is a bit "exotic" and Quiet NaN is your "regular" NaN. So I have my doubts that Python would be passing a Signaling NaN through COM. How could I find out if a Python float('nan') is passed through COM as a Signaling NaN or Quiet NaN, or maybe Indeterminate?

Is there any way to make a Signaling NaN versus QuietNaN or Indeterminate in Python, when interfacing with other languages? (Using ctypes perhaps?) I assume this would be a platform-specific solution, and I'd accept that in this case.

Update: In the TestStand sequence editor, I tried making two variables, one set to NAN and the other set to IND. Then I saved it to a file. Then I opened the file and read each variable using Python. In both cases, Python reads them as a nan float.

+2  A: 

John Cook had a nice post on this that might be helpful:

Update: won't this work?

In [144]: import scipy

In [145]: scipy.nan
Out[145]: 1.#QNAN

In [146]: scipy.inf
Out[146]: 1.#INF

In [147]: scipy.inf * 0
Out[147]: -1.#IND
ars
You probably intended to link [here](http://www.johndcook.com/blog/2009/07/21/ieee-arithmetic-python/).
Stigma
@Stigma: D'oh! Link fixed, thanks.
ars
That's a helpful reference for learning about NAN and INF in Python. Unfortunately, it doesn't go as far as answering my question.
Craig McQueen
`scipy.nan` sounds interesting. Note, on Windows, it is printed simply as `nan`. So if you get `1.#QNAN` I assume you're on Linux. Unfortunately, I'm home sick today, and I have a Windows laptop but it doesn't have TestStand on it to try this.
Craig McQueen
I'm on Windows Vista with python 2.5, scipy 0.8. Not sure if it makes a difference, but it's 64-bit.
ars
+3  A: 

I dug a bit for you, and I think you might be able to use the struct module in combination with the information on at Kevin's Summary Charts. They explain the exact bit patterns used for the various kinds of IEEE 754 floating point numbers.

The only thing you probably will have to be careful for, if I read the topics on this IND-eterminate value, is that that value tends to trigger some kind of floating point interrupt when assigned directly in C code, causing it to be turned into a plain NaN. Which in turn meant those people were advised to do this kind of thing in ASM rather than C since C abstracted that stuff away.. Since it is not my field, and that I am not sure to what extent this kind of value would mess with Python, I figured I'd mention it so you can at least keep an eye for any such weird behaviour. (See the accepted answer for this question).

>>> import struct

>>> struct.pack(">d", float('nan')).encode("hex_codec")
'fff8000000000000'

>>> import scipy
>>> struct.pack(">d", scipy.nan).encode("hex_codec")
'7ff8000000000000'

Referring to Kevin's Summary Charts, that shows that float('nan') is actually technically the Indeterminate value, while scipy.nan is a Quiet NaN.

Let's try making a Signaling NaN, and then verify it.

>>> try_signaling_nan = struct.unpack(">d", "\x7f\xf0\x00\x00\x00\x00\x00\x01")[0]
>>> struct.pack(">d", try_signaling_nan).encode("hex_codec")
'7ff8000000000001'

No, the Signaling NaN gets converted to a Quiet NaN.

Now let's try making a Quiet NaN directly, and then verify it.

>>> try_quiet_nan = struct.unpack(">d", "\x7f\xf8\x00\x00\x00\x00\x00\x00")[0]
>>> struct.pack(">d", try_quiet_nan).encode("hex_codec")
'7ff8000000000000'

So that's how to make a proper Quiet NaN using struct.unpack()--at least, on a Windows platform.

Stigma
Thanks for your answer Stigma. I've taken the liberty of editing it to add code samples. Much appreciated.
Craig McQueen
I'm glad you got out of it what you needed, and thank you for adding the code samples for others like myself to learn from. :)
Stigma
Nice -- thanks.
ars
+1  A: 

CPython definition of nan

When Python reports a nan, where does that come from?

  • Result of a calculation (platform specific values?)
  • Py_NAN in the CPython C source code
    • defined as (Py_HUGE_VAL * 0.)
      • Value is platform-specific
      • Py_HUGE_VAL is probably defined as HUGE_VAL--it has a note to say it should be HUGE_VAL except on platforms where that is broken.
  • float('nan') which is defined from Py_NAN in CPython's C source code.

Reading Python and pywin32 Source Code

I've had a look at the C source code for pywin32, in particular win32com, which forms the Python↔COM translation layer. That code:

  • takes the input object
  • calls PyNumber_Float() to convert it to a Python float (if it isn't already)
  • calls PyFloat_AsDouble() to convert it to a plain C double value.
    • This simply returns the C double directly contained in the PyFloatObject member ob_fval.

So it looks as though I've traced a NaN from the COM interface back to a plain C double type containing Py_NAN, whatever that turns out to be on the Windows platform.

TestStand NAN Value

Now I've tried this with NI TestStand. First I tried:

quiet_nan = struct.unpack(">d", "\x7f\xf8\x00\x00\x00\x00\x00\x01")[0]
# Set the variable's value in TestStand
locals_prop_object.SetValNumber(var_name, 0, quiet_nan)

But that still appeared in TestStand as IND. So then I created a TestStand file with variables set to IND and NAN, and read the values from Python. It turns out that TestStand's NAN has a value of FFFF000000000001. According to Kevin's Summary Charts that is a negative quiet NAN. TestStand's IND does have the expected value for Indeterminate, FFF8000000000000.

Success

So, after all that, I have succeeded in setting a NAN in TestStand, from Python:

# Make a NAN suitable for TestStand
teststand_nan = struct.unpack(">d", "\xff\xff\x00\x00\x00\x00\x00\x01")[0]
# Set the variable's value in TestStand
locals_prop_object.SetValNumber(var_name, 0, teststand_nan)
Craig McQueen
@Craig: did the scipy NaNs not work when passed through COM?
ars
I didn't yet try `scipy.nan` on my computer that runs TestStand.
Craig McQueen
@ars, as I've shown in my updated answer, even `scipy.nan` wouldn't work. A "quiet negative NaN" did the trick.
Craig McQueen
Good to know, thanks for the update!
ars