This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: struct.unpack() returns NaN
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.8
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: jrojas, steven.daprano, terry.reedy
Priority: normal Keywords:

Created on 2021-08-27 17:13 by jrojas, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (3)
msg400434 - (view) Author: Jorge Rojas (jrojas) Date: 2021-08-27 17:13
Hi all!

I have this case when trying to get a float value applying pack to these integer values:
struct.unpack('f', struct.pack('HH', 0, 32704))

This happens when executing the unpack function to a float format, from a bit-array where the sign bit is not in a suitable position I think. Applying big-endian to the format, it returns a numeric value, but being little-endian it returns a NaN.

> struct.unpack('<f', struct.pack('HH',0, 32704))
Out[168]: (nan,)
> struct.unpack('>f', struct.pack('HH',0, 32704))
Out[169]: (6.905458702346266e-41,)

The current documentation on struct.unpack doesn't anything about what conditions a NaN is returned, besides this might be a expected value. Maybe explaining how this value could be converted to an equivalent format to retrieve the proper value may help, or why this returns a NaN and how to avoid it.

Thanks in advance.
msg400451 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2021-08-28 00:39
This is not a bug, and I'm unconvinced that it needs a documentation change. If you are using struct to assemble values, you need to know the meaning of the struct format.

It returns a float NAN because you give it the bit pattern of a NAN.

For C singles and doubles, some bit patterns give numbers, two give signed zeroes, two give signed INFs (infinities), and some give NANs. If your bit pattern happens to be a NAN, you will get a NAN.

That is no more special or unexpected than this:

>>> struct.unpack('>f', struct.pack('HH', 65, 7392))
(8.05471420288086,)
>>> struct.unpack('<f', struct.pack('HH', 65, 7392))
(1.482314221017757e-21,)

If you reverse the bit pattern by changing the endianism, you get a different number.

You already know how to avoid this: if your bit pattern is littlendian, don't pack it into a bigendian struct, and vice versa.

To convert it, you would have to pack the float back into a bit array, reverse the bits, then unpack it. But it's much easier to just get the endianism right in the first place.

It's not the sign bit. Its the whole bit pattern. If it happens to match the bit pattern of a NAN when you reverse it, you get a NAN.

The format of singles are described here:

https://en.wikipedia.org/wiki/Single-precision_floating-point_format
msg400453 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2021-08-28 01:27
Jorge, next time you have questions about how to use a module (and struct is fairly advanced), please ask on an appropriate forum, such as python-list.  Then Steven's nice answer could be read by 100s of people instead of likely less than 10.

I looked as the struct doc to see if I thought it needs anything and decided I agree with Stephen that not.  Struct can pack and unpack various IEEE binary floating point formats.  Anyone doing serious work with such knows that they include infinities and nans.
History
Date User Action Args
2022-04-11 14:59:49adminsetgithub: 89195
2021-08-28 01:27:45terry.reedysetnosy: + terry.reedy
messages: + msg400453
2021-08-28 00:39:42steven.dapranosetstatus: open -> closed

nosy: + steven.daprano
messages: + msg400451

resolution: not a bug
stage: resolved
2021-08-27 17:13:38jrojascreate