「Python3学习笔记」读书笔记--float类型

本文为「Python3学习笔记」一书的读书总结。

在Python中 float 类型默认存储双精度浮点数(也就是其他语言中的 double ),可一表达16到17位浮点数。

>>> 1/3
0.3333333333333333
>>> 0.1234567890123456789
0.12345678901234568

从实现方式上来看,浮点数是以二进制的方式来存储十进制数的近似值。这就可能导致执行的结果与预期不符合,造成不一致缺陷。所以,在对精度有严格要求的场合,应该选择使用固定精度类型,如:decimal.Decimal 。

可通过 float.hex 方法输出实际存储值的十六进制格式的字符串,来查看执行结果为何不同;换句话说,也可以通过改方式实现浮点值的精确传递,避免精度丢失。

>>> 0.1 * 3 == 0.3
False
>>> (0.1 * 3).hex()
'0x1.3333333333334p-2'
>>> (0.3).hex()     # 显然两个存储的内容并不相同
'0x1.3333333333333p-2'
>>> s = (1/3).hex()
>>> float.fromhex(s)        # 反向转换回浮点数
0.3333333333333333

对于简单的比较操作,可尝试将浮点数的精度限制在有效的精度内,如:使用 round函数,但round函数在实现上有一定的问题,这里更加准确的问题是使用 decimal.Decimal 模块。

>>> round(0.1 * 3, 2) == round(0.3, 2)
True
>>> round(0.1, 2) * 3 == round(0.3, 2)
False

decimal.Decimal模块

与 float 这种基于硬件的二进制浮点类型相比,decimal.Decimal 是用十进制实现的,它能准确的表达十进制数和运算,不存在二进制相似值的问题,它最高可提供28位有效精度。

>>> (0.1 + 0.1 + 0.1 - 0.3) == 0        # 二进制近似值计算结果与十进制预期不符合
False
>>> from decimal import Decimal
>>> (Decimal('0.1') + Decimal('0.1') + Decimal('0.1') - Decimal('0.3')) == 0
True

而在创建 Decimal 实例的时候,应该传入一个准确数值,如:整数或者字符串等。如果传入的是 float 类型,那么在传入之前,其精度就已经丢失了。

>>> Decimal(0.1)
Decimal('0.1000000000000000055511151231257827021181583404541015625')
>>> Decimal('0.1')
Decimal('0.1')

在需要的时候,可以通过上下文修改 Decimal 默认的28位精度或用 localcontext修改某个区域的精度。

>>> from decimal import Decimal, getcontext, localcontext

>>> getcontext()
Context(prec=28, ...)
>>> getcontext().prec = 2
>>> Decimal(1) / Decimal(3)
Decimal('0.33')

>>> with localcontext() as ctx:
...     print(getcontext().prec)
...     getcontext().prec = 3
...     print(getcontext().prec)
...     print(Decimal(1) / Decimal(3))
...
2
3
0.333
>>> getcontext().prec
2

Decimal 虽然有着准确的精度,但它的运算速度会慢很多,所以除非有明确的需求,否则还是不要用 Decimal 代替 float 是用。

round 函数

因为精度和近似值问题,在使用round函数对 float 类型的值进行“四舍五入”的操作存在不确定性,结果会有一些不易察觉的陷阱。

>>> round(0.5)      # 五舍
0
>>> round(1.5)      # 五入
2

这是因为round函数的算法规则是按临近的数字的距离远近来考虑是否进位的,如:以 0.4 为例,其舍入后相邻的数字分别是 0 和 1 ,从距离上来看自然是离 0 更近一些,所以“四舍五入”的结果为 0。如此一来,“四舍六入”就是确定的,相关问题都集中在两边距离相等的5是否进位上了。

对于5是否进位,首先要考虑的是它后面是否还有小数位。如果有,那么左右距离自然是不想等的,这种情况肯定是会进位的。

>>> round(0.5)
0
>>> round(0.500001)
1

如果没有,就要看进位后是整数还是浮点数了。如果是整数,就取临近的偶数。

>>> round(0.5)
0
>>> round(1.5)
2

不同的Python版本,规则存在着差异性,如:在Python2.7中,round(2.5) 返回的是3.0。

而进位后,如果依旧是浮点数的话,那事情就变得有点莫名其妙了。有的文章中说的是要看数字5前一位小数的奇偶行来判断是否进位,而事实上并非如此。

>>> round(1.25, 1)      # 偶舍
1.2
>>> round(1.245, 2)     # 偶入
1.25
>>> round(2.675, 2)     # 下面都是奇数7,但却有舍有进
2.67
>>> round(2.375, 2)
2.38

对此,Python官方文档Floating Point Arithmetic: Issues and Limitations宣称着并非错误,而是事出有因。我们可以改用 Decimal ,按需选取可控的进位方案。

转换

在 Python 中将整数或字符串转换为浮点数很简单,而且 Python 还会自动处理字符串内的正负号和空白符。只是超出有效精度时,结果与字符串内容存在差异。

>>> float(100)
100.0
>>> float('-100.123')
-100.123
>>> float('\t -100.123\n')
-100.123
>>> float('1.23E2')
123.0
>>> float('0.12345678901234567890')     # 超出精度
0.12345678901234568

反过来,将浮点数转换为整数时,有多种方案可供选择。

  1. 可直接截掉小数部分
>>> int(2.6), int(-2.6)
(2, -2)

>>> from math import trunc
>>> trunc(2.6), trunc(-2.6)
(2, -2)
  1. 分别向大小两个方向取临近整数
>>> from math import floor, ceil
>>> floor(2.6), floor(-2.6)     # 向小数字方向取最近整数
(2, -3)
>>> ceil(2.6), ceil(-2.6)           # 向大数字方向取最近整数
(3, -2)

从博客搬运到简书:原文链接

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容