自定义NaN浮动在Python和Numpy中的行为

病毒

我需要将一些额外的信息打包到浮点NaN值中。我在Python中使用单精度IEEE 754浮点数(32位浮点数)。Python和NumPy如何处理这些值?

理论

如果设置了指数位(23..30),并且设置了至少一个有效位,则IEEE 754-2008标准似乎认为数字实际上不是数字。因此,如果将浮点数转换为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,则没有错误。

假设

如果我设置is_quiet并将其余的位用于自己的目的,Python和NumPy都会很高兴Python自行处理,NumPy依赖于低级语言实现和/或硬件FP实现。

我的假设是正确的,可以通过某些官方文件证明或否定吗?还是这些依赖平台的事情之一?

我在这里发现了一些很相关的东西:如何在Python中区分不同类型的NaN浮点数,但是我找不到任何关于在Python或NumPy中如何处理携带额外信息的NaN的正式说法。

病毒

考虑了一段时间之后,看了一下源代码广告,然后重新考虑了一下,我想我可以回答我自己的问题。我的假设几乎是正确的,但并非全部。

由于NumPy和Python处理数字的方式大不相同,因此此答案分为两部分。

使用NaN在Python和NumPy中真正发生了什么

NumPy

这可能是特定于平台的,但在大多数平台上,NumPy使用gcc内置的isnan,这反过来又可以快速地完成某些工作。在大多数情况下,运行时警告来自更深的层次,来自硬件。(NumPy可以使用多种确定NaN状态的方法,例如x!= x,它至少可以在AMD 64平台上运行,但是gcc下降到gcc,可能为此目的使用了一些很短的代码。)

因此,从理论上讲,没有办法保证NumPy如何处理NaN,但是在实践中,在更常见的平台上,它将按照标准的规定进行操作,因为这就是硬件的作用。NumPy本身根本不关心NaN类型。(除了某些特定于NumPy的,非硬件支持的数据类型和平台。)

蟒蛇

这里的故事变得有趣。如果平台支持IEEE浮点数(大多数情况下),则Python使用C库进行浮点算术运算,因此在大多数情况下几乎直接使用硬件指令。因此,NumPy应该没有任何区别。

除了...通常在Python中没有32位浮点数。Python浮点对象使用C double,这是一种64位格式。如何在这些格式之间转换特殊的NaN?为了了解实际情况,下面的一些C代码可以帮助您:

/* nantest.c - Test floating point nan behaviour with type casts */

#include <stdio.h>
#include <stdint.h>

static uint32_t u1 = 0x7fc00000;
static uint32_t u2 = 0x7f800001;
static uint32_t u3 = 0x7fc00001;

int main(void)
    {
    float f1, f2, f3;
    float f1p, f2p, f3p;
    double d1, d2, d3;
    uint32_t u1p, u2p, u3p;
    uint64_t l1, l2, l3;

    // Convert uint32 -> float
    f1 = *(float *)&u1; f2 = *(float *)&u2; f3 = *(float *)&u3;

    // Convert float -> double (type cast, real conversion)
    d1 = (double)f1; d2 = (double)f2; d3 = (double)f3;

    // Convert the doubles into long ints
    l1 = *(uint64_t *)&d1; l2 = *(uint64_t *)&d2; l3 = *(uint64_t *)&d3;

    // Convert the doubles back to floats
    f1p = (float)d1; f2p = (float)d2; f3p = (float)d3;

    // Convert the floats back to uints
    u1p = *(uint32_t *)&f1p; u2p = *(uint32_t *)&f2p; u3p = *(uint32_t *)&f3p;

    printf("%f (%08x) -> %lf (%016llx) -> %f (%08x)\n", f1, u1, d1, l1, f1p, u1p);
    printf("%f (%08x) -> %lf (%016llx) -> %f (%08x)\n", f2, u2, d2, l2, f2p, u2p);
    printf("%f (%08x) -> %lf (%016llx) -> %f (%08x)\n", f3, u3, d3, l3, f3p, u3p);

    return 0;
    }

打印:

nan (7fc00000) -> nan (7ff8000000000000) -> nan (7fc00000)
nan (7f800001) -> nan (7ff8000020000000) -> nan (7fc00001)
nan (7fc00001) -> nan (7ff8000020000000) -> nan (7fc00001)

通过查看第二行,很明显,我们具有与Python相同的现象。因此,在64位版本中,指数转换之后立即double引入了额外的is_quiet位。

这听起来有些奇怪,但是实际上标准说(IEEE 754-2008,第6.2.3节):

将安静的NaN从较窄的格式转换为相同基数的较宽格式,然后再转换回相同的较窄的格式,除非使其成为规范,否则不应以任何方式更改安静的NaN有效载荷。

这没有说出信号NaN的传播。但是,这在6.2.1节中进行了解释:

对于二进制格式,有效载荷以尾随有效位的p-2个最低有效位进行编码。

上面p是精度,对于32位浮点数为24位。因此,我的错误是使用信号通知的NaN作为有效载荷。

概要

我得到以下要点:

  • IEEE 754-2008支持并鼓励使用qNaN(安静的NaN)
  • 奇怪的结果是因为我尝试使用sNaN,并且类型转换导致将is_quiet位置1
  • NumPy和Python在最常见的平台上均按照IEEE 754进行操作
  • 该实现严重依赖于基础C实现,因此几乎没有保证(Python中甚至有一些代码承认NaN并未像在某些平台上那样被处理)
  • 解决此问题的唯一安全方法是对有效载荷进行一些DIY

但是,有一件事情既没有用Python也没有用NumPy(也没有我遇到的任何其他语言)实现。第5.12.1节:

语言标准应提供将支持的格式的NaN可选转换为外部字符序列的功能,该功能将基本NaN字符序列的后缀附加到代表NaN有效载荷的后缀(请参见6.2)。有效负载后缀的形式和解释是语言定义的。语言标准应要求在将外部字符序列转换为支持的格式时,接受任何此类可选输出序列作为输入。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

自定义UICollectionViewLayout和浮动辅助标题视图崩溃

来自分类Dev

浮动标签自定义

来自分类Dev

Android 中可自定义的浮动按钮

来自分类Dev

Python 3.4中的自定义异常代码和消息

来自分类Dev

Python中的自定义词典和JSON模块

来自分类Dev

用Erlang编写和编译自定义行为

来自分类Dev

自定义投射按钮和行为实现

来自分类Dev

MVC自定义路由和BeginForm行为

来自分类Dev

Shell提示自定义和cmd行为

来自分类Dev

自定义iOS浮动按钮

来自分类Dev

Python-自定义树的异常行为

来自分类Dev

具有自定义哈希行为的 Python 对象集

来自分类Dev

自定义iOS7中的UIButton类的行为

来自分类Dev

symfony2中的自定义异常行为

来自分类Dev

dict子类中的自定义迭代行为

来自分类Dev

自定义函数中的变异行为异常(变异)

来自分类Dev

自定义iOS7中的UIButton类的行为

来自分类Dev

如何在子句行为中自定义NamedParameterJdbcTemplate?

来自分类Dev

Python自定义模块和导入

来自分类Dev

Python自定义标题和图例

来自分类Dev

ROS自定义消息和numpy数组

来自分类Dev

Python中的自定义异常

来自分类Dev

在python中绘制自定义图

来自分类Dev

ElasticSearch中的Python自定义脚本

来自分类Dev

Python中的自定义错误消息

来自分类Dev

Python 3中的自定义排序

来自分类Dev

在python中自定义排序

来自分类Dev

在python中自定义__delattr__

来自分类Dev

python中的自定义帮助