Python 共圆行为

Python 共圆行为,python,sympy,Python,Sympy,我正在使用Python内置解决方案和其他一些外部库(如Symphy)研究不同的舍入方法,在这样做的过程中,我偶然发现了一些需要帮助理解其背后原因的案例 Ex-1: 输出: 1.006 在第一种情况下,使用Python内置的舍入函数,输出是1.006而不是1.007,我可以理解这不是一个错误,因为Python舍入到最接近的偶数,这就是所谓的银行家舍入 这就是为什么我从一开始就开始寻找另一种方法来控制舍入行为。通过快速搜索,我找到了decimal.decimal模块,该模块可以轻松处理十进制值并有效

我正在使用Python内置解决方案和其他一些外部库(如Symphy)研究不同的舍入方法,在这样做的过程中,我偶然发现了一些需要帮助理解其背后原因的案例

Ex-1:

输出: 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():

产出:1.01

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

产出:1.01

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

问题来了:


正如我可以与Decimal objects getcontext()一起使用一样。舍入='ROUND'u HALF\u UP'要更改舍入行为,有没有办法将N和舍入行为改为小数位数而不是有效数字?

而不是在Symphy中重新实现小数舍入,可以使用
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    
0.006
1.007
10.007
100.007

首先,
N
evalf
本质上是一回事
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.007
1.007
10.007
100.007    
0.006
1.007
10.007
100.007

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

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

0.006
1.007
10.007
100.007
>>> 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