回复内容:
因为二进制浮点数不能解决这个问题。先看一个现象,和 round 无关的:
>>> def show(x):
... """打印一个数,20 位精度"""
... print('{:.20f}'.format(x))
...
>>> show(1.5)
1.50000000000000000000
>>> show(1.25)
1.25000000000000000000
>>> show(1.245)
1.24500000000000010658
>>> show(1.45)
1.44999999999999995559
>>> show(1.415)
1.41500000000000003553
四舍五入是基于十进制的,在二进制无法精确表示的时候是会有误差的。任何需要十进制运算的地方,都需要用 decimal.Decimal 取代 float:
>>> Decimal(1.45)
Decimal('1.4499999999999999555910790149937383830547332763671875')
>>> Decimal('1.45')
Decimal('1.45')
>>> Context(prec=2, rounding=ROUND_HALF_UP).create_decimal('1.45')
Decimal('1.5')
>>> Decimal('1.45').normalize(Context(prec=2, rounding=ROUND_HALF_UP))
Decimal('1.5')
>>> Decimal(Decimal('1.45').quantize(Decimal('.1'), rounding=ROUND_HALF_UP))
Decimal('1.5')
Note that this is in the very nature of binary floating-point: this is not a bug in Python, and it is not a bug in your code either. You’ll see the same kind of thing in all languages that support your hardware’s floating-point arithmetic (although some languages may not display the difference by default, or in all output modes).
- 某些十进制数(如0.1)在机器内部无法用有限数目的二进制位0和1精确表示,只能通过增加固定的位数来提高精度,从而更逼近原来十进制数。
round(number[, ndigits])
Return the floating point value number rounded to ndigits digits after the decimal point. If ndigits is omitted, it defaults to zero. The result is a floating point number. Values are rounded to the closest multiple of 10 to the power minus ndigits; if two multiples are equally close, rounding is done away from 0.
- python的round函数定义为 在任意整数*10^(-ndigits) 中取最靠近number的数值,如果有两个整数距离number相等则按照远离0的一侧(负数-0-正数)取值 。
round(0.5) is 1.0 and round(-0.5) is -1.0)
题主测试的和那个bug无关。round是四舍六入五成双的。
python 的官方实现确实有问题,至于为什么作者不改进,原作者可能是什么心态,可以参见刘海洋的答案。大致心态也许是:反正这个错误不在我,所以虽然有方法,但我也不会去解决这个问题。咋一看,很多人会把这个问题理解为浮点数不精确的问题。浮点数不精确,这一点是常识,是对的。
但浮点数并非在所有情况下都不精确,也并非「只要浮点数不精确,所涉及的相关计算问题就毫无解决的价值」。
Python 的 round 问题是个典型的例子,推测作者的观点是认为这类与浮点数精确度有关的问题没有解决的必要。如同 @刘海洋 的观点一样,所以导致了现在的结果。
不过仔细分析会发现,0.5 这种浮点数是可以被精确表示的,而 round 这个函数的特定性在于,round 舍入之后的精确性毫无意义。所以 round 这个问题本身造成的不精确性是可以被解决的。
对于 round 的不精确性,重要的在于结果是进一还是去尾。我写了一个简单的例子来说明这个问题,当然这个函数在特定情况下也不准确,不过在题目给出的情况下都可以得到正确的结果。
#include
float my_round(float src, int idx)
{
int i;
for (i=idx;i--;)
src *=10;
float dest = (int)src;
if (src >= dest+0.5)
dest += 1;
for (i=idx;i--;)
dest /=10;
return dest;
}
int main()
{
printf("result=%f\n", my_round(1.5, 0));
printf("result=%f\n", my_round(1.25, 1));
printf("result=%f\n", my_round(1.245, 2));
printf("result=%f\n", my_round(1.45, 1));
printf("result=%f\n", my_round(1.415, 2));
printf("result=%f\n", my_round(2.675, 2));
}
两个问题,一个是浮点数在计算机中的真实存储(这里是在 Python 中的存储)
expect: 1.0
actual: 1
expect: 1.1
actual: 1.100000000000000088817841970012523233890533447265625
expect: 1.2
actual: 1.1999999999999999555910790149937383830547332763671875
expect: 1.3
actual: 1.3000000000000000444089209850062616169452667236328125
expect: 1.4
actual: 1.399999999999999911182158029987476766109466552734375
expect: 1.5
actual: 1.5
expect: 1.6
actual: 1.600000000000000088817841970012523233890533447265625
expect: 1.7
actual: 1.6999999999999999555910790149937383830547332763671875
expect: 1.8
actual: 1.8000000000000000444089209850062616169452667236328125
expect: 1.9
actual: 1.899999999999999911182158029987476766109466552734375
很简单,因为2.675在表示的时候可能是2.6749,所以round以后还是2.67了
你的需求本质是:精确小数运算。然而,float不是为了满足这一需求而设计的,decimal才是。所以,为float单独定制一个round,不符合float的设计意图,也很难实现。以你的函数为例,temp*10这个操作在float下不是精确的。>>> 1.222*10
12.219999999999999 我的意思是为啥不写个可以解决这个问题的函数:例如(仅仅一个例子,可能从性能啊之类肯定不行,官方是出于什么原因不写一个类似的)
def myround(par,l):
temp = 1
for i in range(l):
temp*=10
v = int((par+0.5/temp)*temp) / temp
return v
i = 1.25
print(myround(i,1))
i = 1.245
print(myround(i,2))
i = 1.21
print(myround(i,1))
i = 1.249
print(myround(i,2))
----
1.3
1.25
1.2
1.25 他的解释应该说的是这个不是个bug。默认数值都是float,float本来就不精确,所以python应该是只对decimal精确。我觉得这个也挺正常的。
相关文章推荐
• Python归纳总结之json标准库• 详细讲解Python之Seaborn(数据可视化)• python基础语法详解之函数• 完全掌握Python数学相关模块• Python详细解析之多线程爬虫与常见搜索算法独孤九贱(3)_JavaScript视频教程
javascript是运行在浏览器上的脚本语言,连续多年,被评为全球最受欢迎的编程语言。是前端开发必备三大法器中,最具杀伤力。如果前端开发是降龙十八掌,好么javascript就是第18掌:亢龙有悔。没有它,你的前端生涯是不完整的。《php.cn独孤九贱(3)-JavaScript视频教程》课程特色:php中文网原创幽默段子系列课程,以恶搞,段子为主题风格的php视频教程!轻松的教学风格,简短的教学模式,让同学们在不知不觉中,学会了javascript知识。
JavaScript教程122232次播放
独孤九贱(6)_jQuery视频教程
jQuery是一个快速、简洁的JavaScript框架。设计的宗旨是“write Less,Do More”,即倡导写更少的代码,做更多的事情。它封装JavaScript常用的功能代码,提供一种简便的JavaScript设计模式,优化HTML文档操作、事件处理、动画设计和Ajax交互。 核心特性可以总结为:具有独特的链式语法和短小清晰的多功能接口;具有高效灵活的css选择器,并且可对CSS选择器进行扩展;拥有便捷的插件扩展机制和丰富的插件。兼容各种主流浏览器,如IE 6.0+、FF 1.5+、Safari 2.0+、Opera 9.0+等,是全球最流行的前端开发框架之一。PHP中文网根据最新版本,独家录制jQuery最新视频教程,回馈PHP中文网的新老用户。
jQuery教程101125次播放
jQuery与Ajax基础与实战
jQuery是最流行的JS函数库,封装了许多实用的功能,其中最引人入胜的就是Ajax。 jQuery中的Ajax操作,语法简单,操作方便,使Ajax从未如此轻松,前端人员从此不再为与服务器异步交互而发愁,本套课程,精选了最常用的几个方法,从基本的语法到每个参数,再到具体实例进行了全面的讲解。
AJAX教程3016次播放
Git教程(60分钟全程无废话版)
Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持
JavaScript教程2310次播放