对于普通的数学舍入,您必须使用第三方模块和 2 个函数:
from decimal import Decimal, ROUND_HALF_UP
d = Decimal(5.445).quantize(Decimal("1.00"), ROUND_HALF_UP)
为什么他们不向内置解释器添加一个仅用于数学舍入而不是银行家舍入的圆形类型函数?并且是否存在在性能和代码可读性方面更简单的舍入选项?
对于普通的数学舍入,您必须使用第三方模块和 2 个函数:
from decimal import Decimal, ROUND_HALF_UP
d = Decimal(5.445).quantize(Decimal("1.00"), ROUND_HALF_UP)
为什么他们不向内置解释器添加一个仅用于数学舍入而不是银行家舍入的圆形类型函数?并且是否存在在性能和代码可读性方面更简单的舍入选项?
这个很难说,可能是因为常见的处理器(x86/ARM)和传统语言/OS(Fortran-2003/C17/C++/POSIX)一样,都支持4种浮点舍入:
floor()
);nearbyint()
,默认舍入,rint()
);int()
);ceil()
);round()
)。真的,这并不妨碍该函数最初
round()
在 C/C++/POSIX 中可用吗?嗯,是的,不是某个团队喜欢它rint()
,但是有些人喜欢它,而且有时它是必要的。我们还有什么其他选择?
标准,适用于
float
(无支撑math.inf
和math.nan
):对于数组和 NumPy 类型:
就可读性而言(即不需要了解数学):
但是它很长,并且
float
速度不是很快。但是,像np_roundTiesToAway()
,它是和的朋友math.inf
,math.nan
一切都符合IEEE-754。Cython 有选项,它们速度更快,并且
round()
从 C(来自 libc)使用,即更可靠。由于
math_roundTiesToAway(x)
(math.trunc(x)
) 是类型int
,因此添加了 的评估float(math_roundTiesToAway(x))
。附言
尽管四舍五入到整数也不错。然而,当涉及到四舍五入到小数点后 N 位时,问题陈述开始变得模棱两可。
如果参数的类型为,那么就像标准 round() 函数
float
一样,由于参数的二进制表示精度有限,您可能会得到稍微意想不到的结果:如果应用的目的
roundTiesToAway()
纯粹是计算/数学的,例如,最低有效数字的均匀分布,或将 0 出现的概率减半,那么这不是问题。一切都严格形式上正确,所有目标都已实现。由于对于该类型来说
Decimal
,10 的幂的乘法是精确的,因此这样的惊喜还有很多,几乎所有的惊喜都是如此。操作中的舍入误差math.fma(x, q, math.copysign(0.5, x))
会稍微降低此类意外发生的可能性,但并不能完全消除。但是,如果目标是获得一个漂亮的结果或者满足其他一些要求,那么问题的陈述可以进行修改,即如果半最低位数字的边界位于
[x - 𝛿, x + 𝛿]
𝛿大于 0 但小于的范围内,则向上舍入math.ulp(x)
(即,如果数字i..i,f..f50..0
等于x
机器精度,则应应用小数舍入规则)。因为你不能只靠类型来解决
float
。不管你在函数内部写什么,这都是行不通的:我们设置任务:需要一个将实数四舍五入到小数点后两位的函数。如果第三位数字正好是
5
,我们就从零开始舍入。必须在图书馆最少参与的情况下解决这个问题。首先要决定的是如何表示参数和结果的类型。
float
——这是自然的答案。问题已经在于类型的选择。我们从中获取代码
Decimal
并使其适应任务:出了点问题。完美的舍入并不完美:5.465被向下舍入,但应该向上舍入为5.47:
因为5.465无法用
float
精确的格式表示。最接近的值是5.4649999999999999857891452847979962825775146484375。由于这个分数严格小于5.465,所以必须向下舍入。第二个较小的问题是我们也无法准确地想象结果
float
。在打印件上您会看到一个整数5.46,在计算机内存中则是5.45999999999999996447286321199499070644378662109375。我们甚至还没有开始编程,但就已经遇到了问题:很难/不可能
float
将四舍五入到两位数并按照用户的预期运行。既然问题是由选择引起的,
float
那你就需要回头去改变你的选择。请记住,我们想要尽可能最简单的解决方案。 Python 标准类型中只剩下一种支持小数的简单类型。这些是线条。也就是说,你需要写:现在你明白为什么标准库中没有舍入函数了吗?