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凑成双数,高位为双数则不进位。

这让我想起了大二时候的大物实验,第一节讲的计数方法,其中印象最深刻的就是“四舍六入五成双”,它的作用是让统计数据更公平,降低舍入的误差

具体查阅:

  1. 浮点算术:争议和限制
  2. 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 

对比

方法耗时(运算一百万次)易读性
使用 decimal1.99 s
使用 math1.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




操作建议

  1. 放到后面再四舍五入
  2. 遵守当地货币规定
  3. 纠结的话就用ROUND_HALF_EVEN




浮点数判等

math.isclose(a, b)




参考文献

  1. Python2 在线工具
  2. Python3 在线工具
  3. 内置函数 — Python文档
  4. How to Round Numbers in Python
  5. math - how to round to higher 10’s place in python
  6. How to round float 0.5 up to 1.0, while still rounding 0.45 to 0.0, as the usual school rounding
  7. Python快速计算函数耗时timeit


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部