12

我一直在玩 C99 的四精度长双精度。据我了解,(特定于平台的)numpy 支持长双精度和 128 位浮点数。

我遇到了一些我无法解释的事情。

鉴于:

>>> import numpy as np

计算一个需要多于 64 位但少于 128 位才能表示为整数的数字:

>>> 2**64+2
18446744073709551618          # note the '8' at the end
>>> int(2**64+2)
18446744073709551618          # same obviously

如果我在 C99 128 位长双精度中计算相同的数字,我得到18446744073709551618.000000

现在,如果我使用 numpy long double:

>>> a=np.longdouble(2)
>>> b=np.longdouble(64)
>>> a**b+a
18446744073709551618.0              # all good...

这些不正确的结果怎么办:

>>> np.longdouble(2**64+2)
18446744073709551616.0             # Note '6'; appears 2**64 not done in long double
>>> np.longdouble(int(2**64+2))
18446744073709551616.0             # can't force the use of a Python long
>>> n=int(2**64+2)
>>> np.longdouble(n)
18446744073709551616.0
>>> np.longdouble(18446744073709551618)
18446744073709551616.0             # It really does not want to do '8' at the end

但是,这有效:

>>> np.longdouble(2**64)+2
18446744073709551618.0

问题:numpy 在将值正确转换为长双精度时是否存在问题?有什么我做错了吗?

4

2 回答 2

10

您正在尝试在不可直接转换的类型之间执行类型转换。看一下堆栈:

#0  0x00002aaaaab243a0 in PyLong_AsDouble ()
   from libpython2.7.so.1.0
#1  0x00002aaaaab2447a in ?? ()
   from libpython2.7.so.1.0
#2  0x00002aaaaaaf8357 in PyNumber_Float ()
   from libpython2.7.so.1.0
#3  0x00002aaaae71acdc in MyPyFloat_AsDouble (obj=0x2aaaaae93c00)
    at numpy/core/src/multiarray/arraytypes.c.src:40
#4  0x00002aaaae71adfc in LONGDOUBLE_setitem (op=0x2aaaaae93c00, 
    ov=0xc157b0 "", ap=0xbf6ca0)
    at numpy/core/src/multiarray/arraytypes.c.src:278
#5  0x00002aaaae705c82 in PyArray_FromAny (op=0x2aaaaae93c00, 
    newtype=0x2aaaae995960, min_depth=<value optimized out>, max_depth=0, 
    flags=0, context=<value optimized out>)
    at numpy/core/src/multiarray/ctors.c:1664
#6  0x00002aaaae7300ad in longdouble_arrtype_new (type=0x2aaaae9938a0, 
    args=<value optimized out>, __NPY_UNUSED_TAGGEDkwds=<value optimized out>)
    at numpy/core/src/multiarray/scalartypes.c.src:2545

如您所见,Python long(无限精度整数)2**64 + 2正在转换为float(即 64 位双精度),这会丢失精度;然后使用 float 初始化 long double 但精度已经丢失。

问题是 128 位双精度不是本机 Python 类型,因此long没有对其进行本机转换,只有 64 位双精度。NumPy 可能会检测到这种情况并使用longC API执行自己的转换,但可能相当复杂,但收益相对较小(您可以np.longdouble从一开始就进行算术运算)。

于 2013-08-30T16:31:16.283 回答
2

NumPy 在 x86 机器上不提供四精度。它确实提供了对 C long double 类型的访问(由编译环境提供;对于 MSVC,这可能是 64 位,对于 GCC,它通常是 80 位)作为 np.longdouble。np.float96 和 np.float128 类型只是填充到 96 或 128 位的长双精度数(用于对齐的内存访问)。请参阅numpy 文档。要在 numpy 中获得四精度,您需要使用硬件平台和编译器,其中 long double 是实际的四精度。

虽然 numpy 可以使用编译器支持(GCC 的float128)或外部库来支持四精度,但这还没有实现。也可以编写一个第三方可导入模块,使其中一个可用,但也没有这样做。

还要注意,即使使用 np.longdouble,也很容易丢失精度:例如,% 运算符强制 numpy 将其数字传递给 python 浮点数,从而丢弃任何额外的精度。

于 2017-03-10T13:55:02.547 回答