【问题标题】:SymPy rounding behaviourSymPy 舍入行为
【发布时间】:2018-02-20 14:28:57
【问题描述】:

我正在研究使用 Python 内置解决方案和 SymPy 等其他一些外部库的不同舍入方法,在此过程中我偶然发现了一些需要帮助来理解其背后原因的案例。

Ex-1:

print(round(1.0065,3))

输出: 1.006

在第一种情况下,使用 Python 内置的舍入函数,输出是 1.006 而不是 1.007,我可以理解这不是错误,因为 Python 会舍入到最接近的偶数,这就是所谓的银行家舍入。

这就是为什么我从一开始就开始寻找另一种控制舍入行为的方法。通过快速搜索,我发现了可以轻松处理十进制值并有效舍入的 decimal.Decimal 模块正在使用 quantize(),如下例所示:

from decimal import Decimal, getcontext, ROUND_HALF_UP
context= getcontext()
context.rounding='ROUND_HALF_UP'

print(Decimal('1.0065').quantize(Decimal('.001')))

输出:1.007

这是一个非常好的解决方案,但唯一的问题是在长数学表达式中硬编码并不容易,因为我需要将每个数字转换为字符串,然后在使用十进制后我将按照表格中的形式传递进动的“0.001”,而不是像内置回合那样直接写“3”。

在寻找另一个解决方案时,我发现 SymPy,我已经在我的脚本中使用了很多,它提供了一些非常强大的功能,可能会有所帮助,但是当我尝试它时,输出并不像我预期的那样。

Ex-1 使用 SymPy sympify():

print(sympify(1.0065).evalf(3))

输出:1.01

Ex-2 使用 SymPy N(标准化):

print(N(1.0065,3))

输出:1.01

首先输出有点奇怪,但经过调查后,我意识到 N 和 sympify 已经正确执行舍入,但舍入到有效数字,而不是小数位。

问题来了:

由于我可以使用 Decimal 对象 getcontext().rounding='ROUND_HALF_UP' 来更改舍入行为,有没有办法更改 N 并将舍入行为简化为小数位而不是有效数字?

【问题讨论】:

    标签: python sympy


    【解决方案1】:

    与其在 SymPy 中重新实现十进制舍入,不如使用 decimal 进行舍入,但将计算隐藏在实用函数中:

    import sympy as sym
    import decimal
    from decimal import Decimal as D
    
    def dround(d, ndigits, rounding=decimal.ROUND_HALF_UP):
        result = D(str(d)).quantize(D('0.1')**ndigits, rounding=rounding)
        # result = sym.sympify(result)  # if you want a SymPy Float
        return result
    
    for x in [0.0065, 1.0065, 10.0065, 100.0065]:
        print(dround(x, 3))
    

    打印

    0.007
    1.007
    10.007
    100.007    
    

    【讨论】:

      【解决方案2】:

      首先Nevalf本质上是一回事; N(x, n) 等于 sympify(x).evalf(n)。在您的情况下,由于 x 是 Python 浮点数,因此使用 N 更容易,因为它表示输入。

      要获得小数点后三位,请使用N(x, 3 + log(x, 10) + 1)。当x在0.1和1之间时,调整log(x, 10) + 1为0;在这种情况下,有效位数与小数点后的位数相同。如果 x 更大,我们会得到更多有效数字。

      例子:

      for x in [0.0065, 1.0065, 10.0065, 100.0065]:
          print(N(x, 3 + log(x, 10) + 1))
      

      打印

      0.006
      1.007
      10.007
      100.007
      

      从 6 到 7 的转变很奇怪,但并不完全令人惊讶。这些数字在二进制系统中没有精确表示,因此截断到最接近的双精度浮点数可能是这里的一个因素。我对此效果做了一些额外的观察on my blog

      【讨论】:

        【解决方案3】:

        evalf 的 n 给出了 x 的前 n 个有效数字(从左侧开始测量)。如果你使用 x.round(3) 它会将 x 舍入到小数点的第 n 位,可以是正数(小数点右侧)或负数(小数点左侧)。

        >>> for x in '0.0065, 1.0065, 10.0065, 100.0065'.split(', '):
        ...     print S(x).round(3)
        0.006
        1.006
        10.007
        100.007
        
        >>> int(S(12345).round(-2))
        12300
        

        【讨论】:

          猜你喜欢
          • 2023-03-24
          • 2022-07-19
          • 2016-01-07
          • 2023-03-22
          • 1970-01-01
          相关资源
          最近更新 更多