文章目录
- [python round(num, n)小数四舍五入](#python round(num, n)小数四舍五入)
-
- [python round(num, n)基础](#python round(num, n)基础)
-
- [银行家舍入(Banker's Rounding)](#银行家舍入(Banker's Rounding))
- 利息被银行四舍五入后,你到底是赚了还是亏了?
- [python小数位的使用decimal模块四舍五入(解决round 遇5不进)](#python小数位的使用decimal模块四舍五入(解决round 遇5不进))
python round(num, n)小数四舍五入
python round(num, n)基础
round函数执行的是标准的四舍五入操作。
bash
> round( number [, ndigits] )
# number:要四舍五入的数,ndigits:要小数点后保留的位数。ndigits为保留的小数位数,不加ndigits则只保留x四舍五入后的整数部分。
round函数的语法结构为:ndigits为小数点后保留的位数、其中number为需要进行四舍五入的数字,round(number,ndigits)。即进行整数舍入、则默认为0,如果ndigits未指定。
然而,对于某些特定的情况,round函数可能会出现不符合预期的行为。
bash
>>> round(2.45, 1)
2.5
>>> round(2.675, 2)
2.67
结果都应该是2.68的,结果它偏偏是2.67,为什么?
原因分析:
这跟浮点数的精度有关。我们知道在机器中浮点数不一定能精确表达,因为换算成一串1和0后可能是无限位数的,机器已经做出了截断处理。那么在机器中保存的2.675这个数字就比实际数字要小那么一点点。这一点点就导致了它离2.67要更近一点点,所以保留两位小数时就近似到了2.67。
例如:
bash
>>> round(3.1456, 2)
3.15
>>> round(3.1415, 2)
3.14
当指定取舍的小数点位数的时候,一般情况也是使用四舍五入的规则,但是碰到.5的情况时,如果要取舍的位数前的小数是奇数,则直接舍弃,如果是偶数则向上取舍。
bash
>>> from decimal import Decimal
>>> import decimal
>>> from decimal import Decimal
>>> print(Decimal(2.6750))
2.67499999999999982236431605997495353221893310546875
>>> print(Decimal(2.675))
2.67499999999999982236431605997495353221893310546875
>>> print(Decimal(2.6751))
2.675100000000000033395508580724708735942840576171875
用二进制转化的是有精度损失.部分小数无法完全用二进制表示,round 本身没有问题,而是二进制保存的值有点误差导致的。
银行家舍入(Banker's Rounding)
银行家舍入法是由IEEE 754标准规定的浮点数取整算法 [1],大部分的编程软件都使用的是这种方法。 所谓银行家舍入法,其实质是一种四舍六入五取偶(又称四舍六入五留双)法。
这不是bug,而是一种常见的舍入法,名称是"银行家式舍入法",
用意是一半舍一半入,如果碰到0.5全入,那么银行觉得自己亏了,
银行希望和用户要风险对半。不光Python,其他的计算机语言都是这个方法
这一方式的另一个常见名称为"银行家舍入",是IEEE754标准的推荐舍入标准。这一方式跟通常的四舍五入相比,平均数方面更能保持原有数据的特性。
四舍六入五考虑,五后非空就进一,五后为空看奇偶,五前为偶应舍去,五前为奇要进一。 其实大多数编程语言在浮点数的运算上或者保留小数位上都是使用的银行家舍入法。
利息被银行四舍五入后,你到底是赚了还是亏了?
涨知识丨利息被银行四舍五入后,你到底是赚了还是亏了?
参考URL: https://m.thepaper.cn/baijiahao_8230554
大家在小学就会学四舍五入对吧,四及以下被舍去,五以及更大的数字则进1。
这个在平时做题的时候没有什么问题,毕竟做错做对也不会来钱。那么问题来了,银行交易的最小单位是分,被小数点约掉的钱虽然不多,但是如果按照我们小学的四舍五入,你和银行到底谁亏谁赚呢?
四舍五入其实有不利于银行,而有利于储户。
真正广泛采用银行家舍入法的,是需要更小误差的科学和计算机系统 ,因此银行家舍入也常常叫做统计学家舍入(statistician's rounding),无偏舍入(unbiased rounding)。
1940年开始,美国材料和试验协会(ASTM)用的就是银行家舍入法。现在大部分编程软件的默认设置都是银行家舍入法,比如C/C++、JavaScript、PHP、Go,英特尔处理器用的也是银行家舍入。
python小数位的使用decimal模块四舍五入(解决round 遇5不进)
decimal模块
在做小数运算或者四舍五入时怎么避免,数据不精确的问题呢?这就要用到Decimal模块。
https://docs.python.org/zh-cn/3/library/decimal.html#rounding-modes
bash
>>> import decimal
>>> from decimal import Decimal
>>> a = "1.345"
>>> a_t = Decimal(a).quantize(Decimal("0.00"), rounding=decimal.ROUND_HALF_UP)
>>> print(a_t)
1.35
四舍五入是基于十进制的,在二进制无法精确表示的时候是会有误差的。
任何需要十进制运算的地方,都需要用 decimal.Decimal 取代 float:
python
from _pydecimal import Decimal, Context, ROUND_HALF_UP
print(Context(prec=3, rounding=ROUND_HALF_UP).create_decimal('1.325'))
- ROUND_HALF_UP 我们熟悉的四舍五入
- ROUND_HALF_EVEN 四舍六入五成双
我们抽象成正常的函数方便使用:
python
import decimal
def normal_round(n, decimal_places):
"""进行正常的四舍五入,并指定保留的小数位数"""
context = decimal.getcontext()
context.rounding = decimal.ROUND_HALF_UP
rounded_value = round(decimal.Decimal(n), decimal_places)
return rounded_value
overall_score = 87.565
grade = normal_round(overall_score, 2)
print(grade)
执行结果:
bash
>>> round(87.565, 2)
87.56
>>> import decimal
>>>
>>> def normal_round(n, decimal_places):
... """进行正常的四舍五入,并指定保留的小数位数"""
... context = decimal.getcontext()
... context.rounding = decimal.ROUND_HALF_UP
... rounded_value = round(decimal.Decimal(n), decimal_places)
... return rounded_value
...
>>> overall_score = 87.565
>>> grade = normal_round(overall_score, 2)
>>> print(grade)
87.56
>>>
注意:normal_round函数返回的是decimal.Decimal类型的对象,而不是浮点数。
数据库操作可能不支持decimal.Decimal类型的参数。对于这种情况,你可以将normal_round函数返回的结果转换为浮点数或字符串,以适应数据库操作的要求。
python
import decimal
def normal_round(n, decimal_places):
"""进行正常的四舍五入,并指定保留的小数位数"""
context = decimal.getcontext()
context.rounding = decimal.ROUND_HALF_UP
rounded_value = round(decimal.Decimal(n), decimal_places)
return float(rounded_value)
在这个修改后的函数中,我们使用 float 函数将 rounded_value 转换为浮点数类型。这样,normal_round 函数将返回一个浮点数,而不是 decimal.Decimal 对象。