Python3的四舍五入round()函数坑爹?不,更科学
文章目录
- 问题描述
- 解决方案
- 进一步阅读
- 强制四舍五入
- 四舍五入策略
- 常用函数
- 向上取整到十位
- 操作建议
- 浮点数判等
- 参考文献
问题描述
Python2 中,round() 的结果就是我们所理解的四舍五入,round(1.5)=2,round(2.5)=3。
Python3 中,round() 有较大改动,round(1.5)=2,而round(2.5)仍然等于2,只有round(2.6)才等于3,这是为什么呢?

解决方案
原来 Python2 的 round() 是四舍五入,而 Python3 的 round() 为四舍六入五成双,即高位为单数则进1凑成双数,高位为双数则不进位。
这让我想起了大二时候的大物实验,第一节讲的计数方法,其中印象最深刻的就是“四舍六入五成双”,它的作用是让统计数据更公平,降低舍入的误差。
具体查阅:
- 浮点算术:争议和限制
- What Every Computer Scientist Should Know About Floating-Point Arithmetic
进一步阅读
由于有浮点精度表示错误,在追求精度的情况下应使用 Decimal,务必用字符串进行实例化
from decimal import Decimalprint(0.29 * 100) # 28.999999999999996
print(Decimal(0.29) * 100) # 28.99999999999999800159855567
print(Decimal('0.29') * 100) # 29.00
print(Decimal('1.5093').quantize(Decimal('1.00'))) # 1.51
强制四舍五入
使用 decimal
from decimal import Decimal, ROUND_HALF_UPdef round(number, ndigits=None):"""强制四舍五入"""exp = Decimal('1.{}'.format(ndigits * '0')) if ndigits else Decimal('1')return type(number)(Decimal(number).quantize(exp, ROUND_HALF_UP))print(round(4.115, 2), type(round(4.115, 2)))
print(round(4.116, 2), type(round(4.116, 2)))
print(round(4.125, 2), type(round(4.125, 2)))
print(round(4.126, 2), type(round(4.126, 2)))
print(round(2.5), type(round(2.5)))
print(round(3.5), type(round(3.5)))
print(round(5), type(round(5)))
print(round(6), type(round(6)))
# 4.12
# 4.12
# 4.13
# 4.13
# 3.0
# 4.0
# 5
# 6
使用 math
import mathdef round(number, ndigits=0):"""强制四舍五入"""exp = number * 10 ** ndigitsif abs(exp) - abs(math.floor(exp)) < 0.5:return type(number)(math.floor(exp) / 10 ** ndigits)return type(number)(math.ceil(exp) / 10 ** ndigits)print(round(4.115, 2), type(round(4.115, 2)))
print(round(4.116, 2), type(round(4.116, 2)))
print(round(4.125, 2), type(round(4.125, 2)))
print(round(4.126, 2), type(round(4.126, 2)))
print(round(2.5), type(round(2.5)))
print(round(3.5), type(round(3.5)))
print(round(5), type(round(5)))
print(round(6), type(round(6)))
# 4.12
# 4.12
# 4.13
# 4.13
# 3.0
# 4.0
# 5
# 6
对比
| 方法 | 耗时(运算一百万次) | 易读性 |
|---|---|---|
使用 decimal | 1.99 s | 高 |
使用 math | 1.21 s | 低 |
四舍五入策略
查看舍入模式,默认为 decimal.ROUND_HALF_EVEN
from decimal import getcontextprint(getcontext())
# Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
切换舍入模式
from decimal import *print(Decimal('1.5093').quantize(Decimal('1.00'), rounding=ROUND_DOWN)) # 临时切换
getcontext().rounding = ROUND_DOWN # 全局切换
所有舍入模式
| 舍入模式 | 功能 |
|---|---|
| decimal.ROUND_CEILING | 向上取整 |
| decimal.ROUND_FLOOR | 向下取整 |
| decimal.ROUND_DOWN | 截断 |
| decimal.ROUND_UP | 舍入到零的反方向 |
| decimal.ROUND_HALF_UP | 舍入到最接近的数,同样接近则舍入到零的反方向(四舍五入) |
| decimal.ROUND_HALF_DOWN | 舍入到最接近的数,同样接近则舍入方向为零 |
| decimal.ROUND_HALF_EVEN | 舍入到最接近的数,同样接近则舍入到最接近的偶数 |
| decimal.ROUND_05UP | 四舍五入,趋近于零 |
import math
from decimal import *# 向上取整
def round_ceiling(n, decimals=0):multiplier = 10 ** decimalsreturn math.ceil(n * multiplier) / multipliergetcontext().rounding = ROUND_CEILING
print('ROUND_CEILING')
print(Decimal('1.32').quantize(Decimal('1.0'))) # 1.4
print(Decimal('-1.32').quantize(Decimal('1.0'))) # -1.3
print(round_ceiling(1.32, 1.0)) # 1.4
print(round_ceiling(-1.32, 1.0)) # -1.3# 向下取整
def round_floor(n, decimals=0):multiplier = 10 ** decimalsreturn math.floor(n * multiplier) / multipliergetcontext().rounding = ROUND_FLOOR
print('ROUND_FLOOR')
print(Decimal('1.32').quantize(Decimal('1.0'))) # 1.3
print(Decimal('-1.32').quantize(Decimal('1.0'))) # -1.4
print(round_floor(1.32, 1.0)) # 1.4
print(round_floor(-1.32, 1.0)) # -1.3# 截断
def round_down(n, decimals=0):multiplier = 10 ** decimalsreturn int(n * multiplier) / multipliergetcontext().rounding = ROUND_DOWN
print('ROUND_DOWN')
print(Decimal('1.32').quantize(Decimal('1.0'))) # 1.3
print(Decimal('-1.32').quantize(Decimal('1.0'))) # -1.3
print(round_down(1.32, 1.0)) # 1.3
print(round_down(-1.32, 1.0)) # -1.3# 将所有值从零进行舍入
getcontext().rounding = ROUND_UP
print('ROUND_UP')
print(Decimal('1.32').quantize(Decimal('1.0'))) # 1.4
print(Decimal('-1.32').quantize(Decimal('1.0'))) # -1.4# 四舍五入到最接近的数字,并通过从0舍入来打破平局
def round_half_up(n, decimals=0):def _round_half_up(n, decimals=0):multiplier = 10 ** decimalsreturn math.floor(n * multiplier + 0.5) / multiplierrounded_abs = _round_half_up(abs(n), decimals)return math.copysign(rounded_abs, n)getcontext().rounding = ROUND_HALF_UP
print('ROUND_HALF_UP')
print(Decimal('1.35').quantize(Decimal('1.0'))) # 1.4
print(Decimal('-1.35').quantize(Decimal('1.0'))) # -1.4
print(round_half_up(1.35, 1.0)) # 1.4
print(round_half_up(-1.35, 1.0)) # -1.4# 舍入到最接近的数,同样接近则舍入方向为零
getcontext().rounding = ROUND_HALF_DOWN
print('ROUND_HALF_DOWN')
print(Decimal('1.35').quantize(Decimal('1.0'))) # 1.3
print(Decimal('-1.35').quantize(Decimal('1.0'))) # -1.3# 舍入到最接近的数,同样接近则舍入到最接近的偶数
getcontext().rounding = ROUND_HALF_EVEN # 默认
print('ROUND_HALF_EVEN')
print(Decimal('15.255').quantize(Decimal('1'))) # 15
print(Decimal('15.255').quantize(Decimal('1.0'))) # 15.3
print(Decimal('15.255').quantize(Decimal('1.00'))) # 15.26# 四舍五入,趋近于零
getcontext().rounding = ROUND_05UP # 四舍五入为0或5则取反方向的值
print('ROUND_05UP')
print(Decimal('1.38').quantize(Decimal('1.0'))) # 1.3
print(Decimal('1.35').quantize(Decimal('1.0'))) # 1.3
print(Decimal('-1.35').quantize(Decimal('1.0'))) # -1.3
print(Decimal('1.49').quantize(Decimal('1.0'))) # 1.4
print(Decimal('1.51').quantize(Decimal('1.0'))) # 1.6
常用函数
| 函数 | 功能 | 例子 |
|---|---|---|
round() | 四舍六入五成双 | round(1.5) == 2 round(2.5) == 2 |
math.ceil() | 向上取整 | math.ceil(1.5) == 2 math.ceil(2) == 2 |
math.floor() | 向下取整 | math.floor(2.5) == 2 math.floor(2) == 2 |
向上取整到十位
from decimal import Decimal, ROUND_UPdef ceil_to_tens_decimal(x):"""向上取整到十位>>> ceil_to_tens_decimal(10.2)Decimal('20')>>> ceil_to_tens_decimal(10.0)Decimal('10')>>> ceil_to_tens_decimal(16.7)Decimal('20')>>> ceil_to_tens_decimal(94.9)Decimal('100')"""return (Decimal(x) / 10).quantize(1, rounding=ROUND_UP) * 10
操作建议
- 放到后面再四舍五入
- 遵守当地货币规定
- 纠结的话就用ROUND_HALF_EVEN
浮点数判等
用 math.isclose(a, b)
参考文献
- Python2 在线工具
- Python3 在线工具
- 内置函数 — Python文档
- How to Round Numbers in Python
- math - how to round to higher 10’s place in python
- How to round float 0.5 up to 1.0, while still rounding 0.45 to 0.0, as the usual school rounding
- Python快速计算函数耗时timeit
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
