高效算术意味着在Python中对小值和大值进行计算

时间:2017-07-29 01:37:29

标签: python performance python-3.x floating-point precision

这不是关于如何计算Python中的平均值的问题,而是在比较两个数字列表的平均值时如何平衡精度和速度的问题。

此问题以学生的成绩为框架,因此要比较的“典型”输入类似于[98, 34, 80][87, 65, 90, 87]。然而,我遇到了一些明显涉及非常大数字的测试用例,因为我偶尔会OverflowError return float(average)

有以下测试用例,使用float()返回错误答案:

x = [9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999,
     9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999,
     9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999]
y = [9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999,
     9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999,
     9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999998]

xy的平均值非常接近,但不相等。从我所看到的,获得正确答案的唯一方法是使用DecimalFraction,但这些速度较慢。

这是一个快速的性能分析。

def mean_fractions(nums):
    return Fraction(sum(nums), max(len(nums), 1))

def mean_builtins(nums):
    return sum(nums) / float(max(len(nums), 1))

def mean_decimal(nums):
    return Decimal(sum(nums)) / max(len(nums), 1)

# test runner
@timeit
def do_itt(func, input, times):
    for i in range(times):
        func(input)

do_ittt(mean_builtins, y, 1000000) # took: 0.9550 sec
do_ittt(mean_decimal, y, 1000000) # took: 3.0867 sec
do_ittt(mean_fractions, y, 1000000) # took: 3.2718 sec

do_ittt(mean_builtins, [96, 43, 88], 1000000) #  took: 0.7679 sec
do_ittt(mean_decimal, [96, 43, 88], 1000000) # took: 1.4871 sec
do_ittt(mean_fractions, [96, 43, 88], 1000000) # took: 2.6341 sec

我们可以看到使用内置提供了显着的加速,甚至忽略了如果您希望最终结果为float,则需要转换DecimalFraction对象。

问题

所以我的问题是,鉴于这些速度差异,有一个很好的方法来了解builtins方法何时适用于某些列表ab,以及何时它会提供错误的答案吗?在上面的xy上,它说它们是相同的,这是错误的,但在[96, 43, 88][87, 50]它可以正常工作精细。

1 个答案:

答案 0 :(得分:2)

假设原始分数始终为整数。 Python float是64位IEEE 754浮点数。这可以表示基数10中15位或更少位数的任何整数,或more precisely,它可以表示任何最大为9,007,199,254,740,993的整数。

因此,如果您的得分总和超过此值,则可能会以您概述的方式使用float时出现问题。

如果像Stefan Pochmann在下面的评论中指出的那样,你有大笔金额但不是那么大,那么你也可能会遇到问题:

6755399441055745.0 / 3 == 6755399441055746.0 / 3 # True

因此,您需要保持在划分结果的15位有效数字的限制之下。如果将15位数字除以3,则“丢失”一位数,因为除法可能不会使整数部分具有更少的位数,并且它需要小数部分的额外数字。这可能意味着单个“备用”有效数字就足够了,但即便这样也不够(我还没有测试过)。但当然,如果得分总和为1千万亿或更高,你会想要使用更高精度的类型。