【问题标题】:How to determine if a decimal fraction can be represented exactly as Python float?如何确定小数部分是否可以完全表示为 Python 浮点数?
【发布时间】:2016-03-26 02:50:24
【问题描述】:

来自 Python tutorial

很遗憾,大多数小数不能完全表示为 二进制分数。结果是,一般来说,小数 您输入的浮点数仅由二进制近似 机器中实际存储的浮点数。

我想知道如何检查给定的小数部分是否将完全表示为 Python float。例如,0.25 可以精确表示,而 0.1 不能:

>>> 0.1 + 0.1 + 0.1 == 0.3
False
>>> 0.25 + 0.25 + 0.25 == 0.75
True

【问题讨论】:

  • 如果是 2 的几分之一?
  • 我已经更新了我的答案,以通过测试提名符是否适合浮点数的指数和分数二进制表示来涵盖 Python 的具体浮点数实现。那些丢失的位以及您删除“已接受”标记的原因可能在哪里? :-)

标签: python floating-point floating-accuracy


【解决方案1】:

就在那一页上:

在当今的大多数机器上,浮点数是使用二进制近似的 分子使用前 53 位开始的分数 最高有效位,分母为 2 的幂。

因此,要精确表示为 float 的小数部分,它必须是分母为 2 的幂的分数:

>>> 1/10 + 1/10 + 1/10 == 3/10  # 0.1 + 0.1 + 0.1
False
>>> 1/4 + 1/4 + 1/4 == 3/4      # 0.25 + 0.25 + 0.25
True

【讨论】:

  • 并且分子的大小必须小于2^53,并且指数必须在范围内(在非正规情况下可能会改变分子的最大值)。这个问题似乎是关于一个充分条件,这个答案提供的必要条件有点弱。
【解决方案2】:

您可以使用fractions module 来检查是否可以表示给定的分数:

from fractions import Fraction

def can_be_represented(num, den):
    f = Fraction(num, den)
    return Fraction.from_float(float(f)) == f

因为浮点数使用二进制分数,您很快就会发现这可以简化为检查分母是 2 的幂:

def can_be_represented(num, den):
    f = Fraction(num, den)
    return f.denominator & (f.denominator - 1) == 0

但是,这不会对分子进行任何边界检查,请通过与来自sys.float_info 的信息进行比较来添加边界检查:

import sys

def can_be_represented(num, den):
    f = Fraction(num, den)
    return (
        # denominator is a power of 2
        f.denominator & (f.denominator - 1) == 0 and
        # numerator exponent can be represented
        f.numerator.bit_length() <= sys.float_info.max_exp and
        # numerator significant bits can be represented without loss
        len(format(f.numerator, 'b').rstrip('0')) <= sys.float_info.mant_dig
    )

以上版本测试:

  • 分母是 2 的幂
  • 分子二进制指数可以表示
  • 分子中包含重要信息的部分可以移动以适合浮点数的尾数。

上述优化但可读性较差的版本是:

def can_be_represented(num, den,
                      _mexp=sys.float_info.max_exp,
                      _mdig=sys.float_info.mant_dig):
    f = Fraction(num, den)
    num, den = f.numerator, f.denominator
    numbl = num.bit_length()
    return (
        # denominator is a power of 2
        den & (den - 1) == 0 and
        # numerator exponent can be represented
        numbl <= _mexp and
        # numerator significant bits can be represented without loss
        (numbl <= _mdig or num << numbl - _mdig >> numbl - _mdig == num)
    )

【讨论】:

  • 如果目的是检查某些东西是否可以表示为 Python 浮点数(而不是简单地用任意精度二进制浮点数精确表示),那么检查分母是否是 2 的幂不是足够了:您还需要检查分子是否有适当的界限。 (并且避免了下溢和上溢。)作为一个简单的反例,请考虑案例10**23
  • @MarkDickinson:对,所以简化不适用。我现在添加了对分子位长度和移位距离的边界检查。
【解决方案3】:

在 Squeak Smalltalk 中,您会找到这种方法:

Fraction>>isAnExactFloat
    "Answer true if this Fraction can be converted exactly to a Float"
    ^ denominator isPowerOfTwo
        and: ["I have a reasonable significand: not too big"
            numerator highBitOfMagnitude <= Float precision
                and: ["I have a reasonable exponent: not too small"
                    Float emin + denominator highBitOfMagnitude <= Float precision]]

Integer>>isAnExactFloat
    "Answer true if this Integer can be converted exactly to a Float"
    | h |
    (h := self highBitOfMagnitude) <= Float precision
        ifTrue: [^ true].
    ^ h - 1 <= Float emax
        and: [h - self abs lowBit < Float precision]

当然不是 Python,但它是相同的底层浮点,所以翻译起来应该不难......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-18
    • 2015-12-24
    • 1970-01-01
    • 2010-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多