python中round(val*10**ndigits)和round(val,ndigits)的失败案例有哪些?

2024-06-07 08:32:36 发布

您现在位置:Python中文网/ 问答频道 /正文

我的软件的用户抱怨说,在某些情况下,存在明显的舍入错误(由于浮点表示问题):

>>> round(4.55, 1)
4.5
>>> '{:.60f}'.format(4.55)
'4.549999999999999822364316059974953532218933105468750000000000'

我正在考虑将当前的舍入功能替换为以下功能:

>>> def round_human(val, ndigits):
...     return round(val * 10 ** ndigits) / 10 ** ndigits
... 
>>> round_human(4.55, 1)
4.6

或者(里面的repr让我感到不安,但由于此时数字已经通过了numpy,我不确定还有什么更好的选择):

>>> def round_decimal(val, ndigits):
...     return float(Decimal(repr(val)).quantize(Decimal(10) ** -ndigits))
... 
>>> round_decimal(4.55, 1)
4.6

是否存在这样的情况:这些函数中的任何一个都会产生圆形的结果,而这些结果在人的检查中看起来是错误的?我不担心ndigits大于3的情况。你知道吗

总的来说,有更好的方法吗?你知道吗


Tags: 用户功能return软件def错误情况val
2条回答

我意识到我可以编写一个测试来强制所有有趣的案例。下面测试中的奇数print语句生成golden_dict,然后手动检查它是否有所需的行为。你知道吗

def test_rounding(self):
    print '        golden_dict = {'

    golden_dict = {
        ('1.005', 2): 1.01,
        ('1.015', 2): 1.02,
        # ...
        ('1.95', 1): 2.0,
    }

    try:
        for a, b, c in itertools.product(range(10), range(10), range(10)):
            s = '1.{}{}{}'.format(a, b, c).rstrip('0')
            self.assertEqual(s.lstrip('+'), repr(float(s)).rstrip('0'))

            for ndigits in [1, 2, 3]:
                q = decimal.Decimal('0.{}1'.format('0' * (ndigits-1)))
                g = golden_dict.get((s, ndigits), round(float(s), ndigits))

                rdp = show.round_decimal(float(s), ndigits)
                rdn = show.round_decimal(float('-' + s), ndigits)

                try:
                    self.assertEqual(rdp, -rdn)
                    self.assertEqual(rdp, g, \
                            "{}: {} != {}".format(s, rdp, g))
                except:
                    print '            ({!r:6}, {!r}): {!r},'\
                            .format(s, ndigits, rdp)
                    # Comment this raise out to produce the 
                    # entire golden_dict all at once.
                    raise

    finally:
        print '        }'

最有效的功能是:

def round_decimal(val, places):
    s = repr(val)
    if places < 1:
        q = decimal.Decimal(10 ** -places)
    else:
        q = decimal.Decimal('0.{}1'.format('0' * (places-1)))
    f = float(decimal.Decimal(s).quantize(q, rounding=decimal.ROUND_HALF_UP))

    return f

这依赖于python神奇的repr(4.55)行为,这里描述:https://docs.python.org/2/tutorial/floatingpoint.htmlhttps://bugs.python.org/issue1580(最后一个是传奇)。你知道吗

您可以使用以下函数进行舍入;它通常比round()本身更有效:

def my_round(x):
    return int(x*10+(0.5 if x > 0 else -0.5))/10

相关问题 更多 >

    热门问题