如何确定小数部分是否可以完全表示为Python float?

时间:2015-12-19 14:25:04

标签: python floating-point floating-accuracy

来自Python tutorial

  

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

我想知道如何检查给定的小数部分是否完全表示为Python float。例如,0.25可以恰好表示0.1可以&#t; t:

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

3 个答案:

答案 0 :(得分:3)

在那页上:

  

在今天的大多数机器上,使用二进制来近似浮点数   分数使用前面的53位开头的分子   最重要的一点,分母作为两个权力。

因此,对于小数部分可以精确表示为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

答案 1 :(得分:3)

您可以使用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)
    )

答案 2 :(得分:1)

在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,但它是相同的底层浮点,所以不应该那么难翻译......