Мне нужно упаковать некоторую дополнительную информацию в значения NaN с плавающей запятой. Я использую float IEEE 754 с одной точностью (32-битные поплавки) в Python. Как Python и NumPy обрабатывают эти значения?
Теория
Стандарт IEEE 754-2008, кажется, считает, что число действительно не является числом, если установлены биты экспоненты (23..30), и по крайней мере один из значащих бит установлен. Таким образом, если мы преобразуем float в 32-битное целочисленное представление, то все, что удовлетворяет следующим условиям:
-
i & 0x7f800000 == 0x7f800000
-
i & 0x007fffff != 0
Это оставило бы меня с большим выбором. Тем не менее, стандарт, кажется, говорит, что старший бит значимости is_quiet и должен быть установлен, чтобы избежать исключений в вычислениях.
Практические тесты
Python 2.7
Чтобы убедиться, я провел несколько тестов с интересными результатами:
import math
import struct
std_nan = struct.unpack("f4", struct.pack("I", 0x7fc00000))[0]
spec_nan = struct.unpack("f4", struct.pack("I", 0x7f800001))[0]
spec2_nan = struct.unpack("f4", struct.pack("I", 0x7fc00001))[0]
print "{:08x}".format(struct.unpack("I", struct.pack("f4", std_nan))[0])
print "{:08x}".format(struct.unpack("I", struct.pack("f4", spec_nan))[0])
print "{:08x}".format(struct.unpack("I", struct.pack("f4", spec2_nan))[0])
Это дает:
7fc00000
7fc00001 <<< should be 7f800001
7fc00001
Это и некоторые дополнительные тесты, похоже, предполагают, что что-то (struct.unpack
?) всегда устанавливает бит is_quiet.
NumPy
Я пробовал то же самое с NumPy, потому что там я всегда могу полагаться на конверсии, не меняя ни одного бита:
import numpy as np
intarr = np.array([0x7f800001], dtype='uint32')
f = np.fromstring(intarr.tostring(), dtype='f4')
print np.isnan(f)
Это дает:
RuntimeWarning: invalid value encountered in isnan
[True]
но если значение заменено на 0x7fc00001
, ошибки нет.
Гипотезы
Оба Python и NumPy будут довольны, если я установлю is_quiet и использую остальные биты для своих целей. Python обрабатывает бит сам по себе, NumPy полагается на языковые реализации более низкого уровня и/или на реализацию аппаратного FP.
Вопрос
Является ли моя гипотеза правильной, и может ли она быть доказана или опровергнута какой-либо официальной документацией? Или это одна из тех вещей, которые зависят от платформы?
Я нашел здесь что-то очень важное: Как отличить разные типы NaN-плавающих в Python, но я не мог найти никакого официального слова о том, как переносить дополнительную информацию NaNs следует обрабатывать в Python или NumPy.