【问题标题】:How to add to/subtract from float value the smallest possible value in pure python [explained how is different]?如何在纯python中添加/减去浮点值的最小可能值[解释有什么不同]?
【发布时间】:2013-11-27 23:11:49
【问题描述】:

此问题仅适用于 Python 程序员。这个问题是not duplicate not working Increment a python floating point value by the smallest possible amount 见底部解释。


我想为任何浮点数添加/减去一些最小值,这将改变这个浮点值大约mantissa/significant part 的一位。如何在纯 Python 中高效地计算这么小的数字。

例如我有这样的 x 数组:

xs = [1e300, 1e0, 1e-300]

生成最小值的功能是什么?所有断言都应该是有效的。

for x in xs:
  assert x < x + smallestChange(x)
  assert x > x - smallestChange(x)

考虑1e308 + 1 == 1e308,因为1 确实意味着0 用于尾数,所以`smallestChange' 应该是动态的。

纯 Python 解决方案将是最好的。


为什么这不是 Increment a python floating point value by the smallest possible amount 的重复项 - 两个简单的测试证明它的结果无效。

(1)Increment a python floating point value by the smallest possible amount区别没有回答问题:

Increment a python floating point value by the smallest possible amount 不工作试试这个代码:

import math
epsilon  = math.ldexp(1.0, -53) # smallest double that 0.5+epsilon != 0.5
maxDouble = float(2**1024 - 2**971)  # From the IEEE 754 standard
minDouble  = math.ldexp(1.0, -1022) # min positive normalized double
smallEpsilon  = math.ldexp(1.0, -1074) # smallest increment for doubles < minFloat
infinity = math.ldexp(1.0, 1023) * 2

def nextafter(x,y):    
    """returns the next IEEE double after x in the direction of y if possible"""
    if y==x:
       return y         #if x==y, no increment

    # handle NaN
    if x!=x or y!=y:
        return x + y       

    if x >= infinity:
        return infinity

    if x <= -infinity:
        return -infinity

    if -minDouble < x < minDouble:
        if y > x:
            return x + smallEpsilon
        else:
            return x - smallEpsilon  

    m, e = math.frexp(x)        
    if y > x:
        m += epsilon
    else:
        m -= epsilon

    return math.ldexp(m,e)  
print nextafter(0.0, -1.0), 'nextafter(0.0, -1.0)'
print nextafter(-1.0, 0.0), 'nextafter(-1.0, 0.0)'

Increment a python floating point value by the smallest possible amount 的结果无效:

>>> nextafter(0.0, -1)
0.0

应该是非零的。

>>> nextafter(-1,0)
-0.9999999999999998

应该是'-0.9999999999999999'。

(2) 没有询问如何加/减最小值,而是询问如何在特定方向上加/减值 - 建议解决方案需要知道 x 和 y。这里只需要知道x。

(3) Increment a python floating point value by the smallest possible amount 中提出的解决方案不适用于边界条件。

【问题讨论】:

  • 如果您关心这一点,那么您可能在 numpy 非常有帮助的领域工作。 numpy.nextafter 对你有用吗?您可以使用numpy.nextafter(x, numpy.inf) 始终保持积极态度。
  • 我不想使用 numpy 来减少内存占用,但可能需要学习 numpy 代码来提取小函数。我更喜欢使用简单的 python 模块。
  • 减少内存占用?在我的机器上导入 NumPy 只需要大约 8 MB。你在写什么样的程序?
  • 当然对于简单的 PC 没关系我会使用 GAE 一些额外的 +8Mb 将花费和加载时间会更长 - 这是不同的规模你不能知道这一点 :) 如果我将使用 PC我不会像你一样在乎......

标签: python python-2.7


【解决方案1】:
>>> (1.0).hex()
'0x1.0000000000000p+0'
>>> float.fromhex('0x0.0000000000001p+0')
2.220446049250313e-16
>>> 1.0 + float.fromhex('0x0.0000000000001p+0')
1.0000000000000002
>>> (1.0 + float.fromhex('0x0.0000000000001p+0')).hex()
'0x1.0000000000001p+0'

只需使用相同的符号和指数。

【讨论】:

  • 你的方法很好。我发现使用sys 和math 没有使用frexp 和ldexp 和sys.float_info.mant_dig 进行字符串转换的速度更快。
  • 我做了一些测试但没有完成代码,结果是在优化和测试之前的时间是hex about 85.5520534034和frexp 19.9034547687。
【解决方案2】:

Mark Dickinson's answer to a duplicate 的表现要好得多,但仍然无法为参数 (0, 1) 提供正确的结果。

这可能是纯 Python 解决方案的一个很好的起点。然而,在所有情况下都做到这一点并不容易,因为有很多极端情况。所以你应该有一个非常好的单元测试套件来涵盖所有的极端情况。

只要有可能,您应该考虑使用基于经过充分测试的 C 运行时函数的解决方案之一(即通过ctypes 或numpy)。

您在某处提到您担心numpy 的内存开销。但是,这一项功能对您的工作集的影响非常小,肯定不是几兆字节(可能是虚拟内存或私有字节。)

【讨论】:

  • 首先1e16 对于1e308 来说太小了,它将不可见。为什么尾数是53 bits,指数是10 bits所以是1024 in base2所以最小值是0x1p971。检查此(float.fromhex('0x1.1ccf385ebc8a0p+1023') - float.fromhex('0x1.1ccf385ebc89fp+1023')).hex() 以十进制表示法1.99584030953472e+292。最小尾数变化取决于可以是 53 位 2.220446049250313e-16(如果第一位设置为 1)。
  • 错误的简单示例:-0x1.ffffffffffffep-1 -0x1.0000000000000p+0 nextafter(-1.0, 0.0)。为什么要比较 float.fromhex('-0x1.ffffffffffffep-1') float.fromhex('-0x1.fffffffffffffp-1') float.fromhex('-0x1.0000000000000p+0') - float.fromhex('-0x1.ffffffffffffep-1') 和 float.fromhex('-0x1.0000000000000p+0') 之间的值是 float.fromhex('-0x1.fffffffffffffp-1')。
  • 那么对于哪个输入,您会得到错误的结果?显示实际结果和预期结果。
  • 例如第一个-0x1.ffffffffffffep-1 -0x1.0000000000000p+0 nextafter(-1.0, 0.0) - 应该是-0x1.fffffffffffffp-1。
  • 是的,原因很容易看出。你试过这个stackoverflow.com/a/10426033/49793这仍然不能正确处理0。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-17
  • 1970-01-01
  • 1970-01-01
  • 2018-09-13
  • 1970-01-01
相关资源
最近更新 更多