【问题标题】:What causes Python's float_repr_style to use legacy?是什么导致 Python 的 float_repr_style 使用 legacy?
【发布时间】:2015-04-28 13:01:22
【问题描述】:

在几乎所有系统上,Python 都可以为您提供人类可读的简短浮点表示,而不是 17 位机器精度:

Python 3.3.0 (default, Dec 20 2014, 13:28:01) 
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 0.1
0.1
>>> import sys; sys.float_repr_style
'short'

ARM926EJ-S 上,您不会得到简短的表示:

Python 3.3.0 (default, Jun  3 2014, 12:11:19) 
[GCC 4.7.3] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 0.1
0.10000000000000001
>>> import sys; sys.float_repr_style
'legacy'

Python 2.7 显然在 repr() 中添加了这个简短的表示,对于大多数系统

浮点数和字符串之间的转换现在可以在大多数平台上正确四舍五入。这些转换发生在许多不同的地方:浮点数和复数上的 str(); float 和 complex 构造函数;数字格式;使用 marshal、pickle 和 json 模块序列化和反序列化浮点数和复数;解析 Python 代码中的浮点数和虚数文字;和小数到浮点数的转换。

与此相关,浮点数 x 的 repr() 现在返回基于最短十进制字符串的结果,该字符串保证在正确舍入下返回到 x(使用半舍入到偶数舍入模式) .以前它给出了一个基于将 x 舍入为 17 位十进制数字的字符串。

负责此改进的舍入库可在 Windows 和 Unix 平台上使用 gcc、icc 或 suncc 编译器运行。 可能有少数平台无法保证此代码的正确运行,因此该代码未在此类系统上使用。您可以通过检查 sys.float_repr_style 来找出正在使用的代码,如果新代码正在使用,它将很短,如果没有,则为旧代码。

由 Eric Smith 和 Mark Dickinson 使用 David Gay 的 dtoa.c 库实现; issue 7117.

他们说某些平台不能保证正确操作(我假设是dtoa.c),但不要说是哪个平台限制导致了这种情况。

ARM926EJ-S 是怎么回事,不能使用短浮点 repr()?

【问题讨论】:

  • 问题:您使用的是旧的 ABI (OABI) 吗? float.__getformat__('double') 在该平台上返回什么?
  • 哦,还有一个测试:1e16 + 2.9999 在您的平台上给出了什么结果? (对我来说,它给出了1.0000000000000002e+16。)
  • 1.获取双倍格式:'IEEE, little-endian'。 2. 1e16:1.0000000000000002e+16
  • 谢谢。这两者都没有明显的问题。你自己构建 Python 吗?如果是这样,您可以查看由configure 脚本生成的pyconfig.h 文件以了解发生了什么。如果没有,这似乎是一个问题,您应该与构建该版本 Python 的人讨论。
  • 它来自 buildroot。

标签: python floating-point


【解决方案1】:

简短的回答:这可能不是平台的限制,而是 Python 构建机制的限制:它没有为浮点计算设置 53 位精度的通用方法。

有关更多详细信息,请查看 Python 源代码分发中的 Include/pyport.h 文件。摘录如下:

/* If we can't guarantee 53-bit precision, don't use the code
   in Python/dtoa.c, but fall back to standard code.  This
   means that repr of a float will be long (17 sig digits).

   Realistically, there are two things that could go wrong:

   (1) doubles aren't IEEE 754 doubles, or
   (2) we're on x86 with the rounding precision set to 64-bits
       (extended precision), and we don't know how to change
       the rounding precision.
 */

#if !defined(DOUBLE_IS_LITTLE_ENDIAN_IEEE754) && \
    !defined(DOUBLE_IS_BIG_ENDIAN_IEEE754) && \
    !defined(DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754)
#define PY_NO_SHORT_FLOAT_REPR
#endif

/* double rounding is symptomatic of use of extended precision on x86.  If
   we're seeing double rounding, and we don't have any mechanism available for
   changing the FPU rounding precision, then don't use Python/dtoa.c. */
#if defined(X87_DOUBLE_ROUNDING) && !defined(HAVE_PY_SET_53BIT_PRECISION)
#define PY_NO_SHORT_FLOAT_REPR
#endif

基本上,有两件事可能会出错。一是 Python 配置无法识别 C double 的浮点格式。该格式几乎总是 IEEE 754 binary64,但有时配置脚本无法弄清楚这一点。这是上面 sn-p 中的第一个 #if 预处理器检查。查看编译时生成的pyconfig.h 文件,看看DOUBLE_IS_... 宏中是否至少有一个是#defined。或者,在 Python 提示符下尝试:

>>> float.__getformat__('double')
'IEEE, little-endian'

如果你看到类似上面的内容,这部分应该没问题。如果您看到类似 'unknown' 的内容,则表明 Python 无法识别浮点格式。

第二个可能出错的地方是我们确实有 IEEE 754 binary64 格式的双精度,但 Python 的构建机制无法弄清楚如何确保该平台的浮点计算的 53 位精度。 dtoa.c 源要求我们能够以 53 位的精度执行所有浮点运算(无论是在硬件还是软件中实现)。这在使用 x87 浮点单元进行双精度计算(与较新的 SSE2 指令相反)的英特尔处理器上尤其成问题:x87 的默认精度为 64 位,并将其用于双精度计算使用该默认精度设置会导致double rounding,这打破了dtoa.c 假设。因此,在配置时,构建机器会检查 (1) 双舍入是否是一个潜在问题,以及 (2) 如果是,是否有办法将 FPU 设置为 53 位精度。所以现在您想查看pyconfig.h 中的X87_DOUBLE_ROUNDINGHAVE_PY_SET_53BIT_PRECISION 宏。

所以它可能是上述任何一种。如果我不得不猜测,我猜在那个平台上,双舍入被检测为一个问题,并且不知道如何解决它。在这种情况下,解决方案是调整 pyport.h 以以任何特定于平台的方式定义 _Py_SET_53BIT_PRECISION_* 宏以获得 53 位精度模式,然后定义 HAVE_PY_SET_53BIT_PRECISION

【讨论】:

  • 嗯。为什么 C sn-p 上的语法突出显示如此可怕?看起来好像荧光笔已经猜到它是某种其他语言。
  • "是否有办法将 FPU 置于 53 位精度" -- ARM926 上缺少 FPU 是否会影响或导致此问题?据我所知,这款芯片没有FPU。
  • 不,不应该。用软件实现的浮点应该没问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-25
  • 2012-04-19
  • 1970-01-01
  • 2013-08-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多