Python 浮点数计算 小数取舍指南

浮点算术:争议和限制

浮点数在计算机硬件中表示为以 2 为基数(二进制)的小数。

举例而言,十进制的小数

0.125=1/10 + 2/100 + 5/1000 

同理二进制小数

0.001

0*1/2+0*1/4+1*1/8得十进制的0.125(二进制的小数转换为十进制主要是乘以2的负次方)

这两个小数具有相同的值,唯一真正的区别是第一个是以 10 为基数的小数表示法,第二个则是 2 为基数。

大多数的十进制小数都不能精确地表示为二进制小数。这导致在大多数情况下,你输入的十进制浮点数都只能近似地以二进制浮点数形式储存在计算机中。

用十进制来理解这个问题显得更加容易一些。考虑分数 1/3 。

我们可以得到它在十进制下的一个近似值 0.3

或者,更近似的,:0.33

或者,更近似的,:0.333

以此类推。结果是无论你写下多少的数字,它都永远不会等于 1/3 ,只是更加更加地接近 1/3 。

同样的道理,无论你使用多少位以 2 为基数的数码,十进制的 0.1 都无法精确地表示为一个以 2 为基数的小数。 在以 2 为基数的情况下, 1/10 是一个无限循环小数

0.0001100110011001100110011001100110011001100110011...

计算机只认识二进制,十进制之间的运算,需要将十进制转换为二进制在进行二进制之间的运算。

因为 Python 只会打印计算机中存储的二进制值的十进制近似值

>>>print(0.1+0.1+0.1)
0.30000000000000004

虽然病态的情况确实存在,但对于大多数正常的浮点运算使用来说,你只需简单地将最终显示的结果舍入为你期望的十进制数值即可得到你期望的结果。 str() 通常已足够,对于更精度的控制可参看 格式字符串语法 中 str.format() 方法的格式描述符。

对于需要精确十进制表示的使用场景,请尝试使用 decimal 模块,该模块实现了适合会计应用和高精度应用的十进制运算(数学上的四舍五入)

#新Decimal的重要性仅由输入的位数决定。 上下文精度和舍入仅在算术运算期间发挥作用。>>>print(decimal.Decimal('0.01')+decimal.Decimal('0.01')+decimal.Decimal('0.01'))
0.03
总是输入 decimal.Decimal('0.01')过于笨拙。 
可将构造器简写为一个字母:
>>>D = decimal.Decimal
>>>D('0.01') + D('0.01') + D('0.01')

如何正确进行四舍五入

来测试一下,0.125 ,0.375 分别保留两位小数是多少:
print(decimal.Decimal('0.125').quantize(Decimal('.01')))  0.12
print(decimal.Decimal('0.375').quantize(Decimal('.01')))  0.38
来并未达到预期效果,

我们可以通过指定quantize() 中 rounding参数来确定进位方式。如果没有指定 rounding参数,那么默认使用上下文提供的进位方式。rounding=ROUND_HALF_EVEN

ROUND_HALF_EVEN实际上就是 奇进偶舍!如果要指定真正的四舍五入,那么我们需要在 quantize中指定进位方式为 ROUND_HALF_UP

quantize(Decimal('.01'), rounding=decimal.ROUND_HALF_UP)

  Python的官方中文文档 decimal --- 十进制定点和浮点运算 — Python 3.10.2 文档

关于使用round函数

round(number[, ndigits])

返回 number 舍入到小数点后 ndigits 位精度的值。 如果 ndigits 被省略或为 None,则返回最接近输入值的整数。

对于支持 round() 方法的内置类型,结果值会舍入至最接近的 10 的负 ndigits 次幂的倍数;如果与两个倍数同样接近,则选用偶数。因此,round(0.5) 和 round(-0.5) 均得出 0 而 round(1.5) 则为 2ndigits 可为任意整数值(正数、零或负数)。如果省略了 ndigits 或为 None ,则返回值将为整数。否则返回值与 number 的类型相同。

round对小数的精确度采用了 四舍六入五成双的方式。( 奇进偶舍的处理方法)

其具体要求举例如下(以保留两位小数为例):一个小数 a.bcd

  1. 如果 d小于5,直接舍去,则舍去。例如:5.214保留两位小数为5.21。

  2. 如果 d大于5,直接进位。例如5.216保留两位小数为5.22。

  3. 如果 d等于5:而且d后面不再有数,要根据应看尾数“5”的前一位C决定是舍去还是进入: 如果是奇数进入,如果是偶数舍去。例如5.215保留两位小数为5.22; 5.225保留两位小数为5.22。

  4. 如果 d等于5:而且d后面仍有数。例如5.2254保留两位小数为5.23,也就是说如果5后面还有数据,则无论奇偶都要进入。

四舍五入(保留2位小数,只舍不入)

通过计算的途径,很难将最终结果截取2位,通过字符串,直接截取就可以了。

>>> a = 12.345

>>> str(a).split('.')[0] + '.' + str(a).split('.')[1][:2]

'12.34'
def get_float(f_str,n):
    a, b, c = f_str.partition('.')
    c = c[:n]
    return ".".join([a, c])
if __name__ == '__main__':
    print(get_float("12.345", 2))
#使用re模块
a = 12.345
re.findall(r"\d{1,}?\.\d{2}", str(a))

四舍五入(保留2位小数,只入不舍)

不根据小数位第三位进行四舍五入,只要后面有值就进行入

print(decimal.Decimal('0.370001').quantize(Decimal('.01'), rounding=decimal.ROUND_UP))
0.38

根据小数位第三位进行四舍五入

先截取成三位小数再进行 rounding=decimal.ROUND_UP

向上/下取整(ceil(a)函数与floor(a)函数)

import math

# 向上取整 3
import math
print("math.ceil(2.3) => ", math.ceil(2.3))
print("math.ceil(2.6) => ", math.ceil(2.6))
# 向下取整 2
print("math.floor(2.3) => ", math.floor(2.3))
print("math.floor(2.6) => ", math.floor(2.6))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天草柑橘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值