最新要闻
- 【新视野】半导体设备板块快速走高,金海通涨停
- 《Redfall》引发粉丝强烈不满
- 环球焦点!《名侦探柯南》新剧场版上映 柯哀亲了登顶热搜!新兰CP党表示要弃剧
- 上海车展全球首发 比亚迪B级纯电SUV官宣:命名“宋L”
- 焦点快看:女子团购住民宿 结果住到自己家的房子 网友:王思聪有这体验
- 【全球快播报】荣耀Magic5成旗舰标杆!赵明:从来没有友商敢挑战它的续航、屏幕
- 环球速看:中标 | 含塔筒3200-3438元/kW!海装、金风、电气风电预中标山东能源渤中海上风电项目
- 全球微头条丨苹果iPhone 15 Pro机型再曝光:依然实体音量键 静音拨片改为Action按钮
- 苹果和安卓折腾了5年 愣是干不过微信小程序
- 抢先看!迪士尼更新真人版《小美人鱼》预告:黑小美人鱼海下展唯美瞬间
- 世界看热讯:标致408保价期内降价遭集体投诉 车主:4S店称地方补贴不算降价
- 【全球聚看点】现场办公面对面听意见解难题 西固区定制化服务助企纾困
- 每日热门:清华、北大在校生五一淄博免费游”?官方回应:消息属实
- 快讯:巴奴“天价土豆”换名后重新上架:多地售价18元一份 北京上海22元
- 云南16岁女孩载21岁女孩无证驾驶摩托车身亡 生前网友曾劝危险
- 天天观热点:张朝阳建议年轻人多做饭、早上6点就起:外卖都是垃圾!王石唱反调
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
四舍五入 - 逼疯全世界的开发者
一、说明
我们先来看一组例子
(资料图)
【PYTHON】
Python 3.6.10 (default, Apr 6 2021, 21:58:27) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linuxType "help", "copyright", "credits" or "license" for more information.>>> >>> round(1.45,1)1.4>>> round(1.55,1)1.6
【JAVA】
public static void main(String[] args) { System.out.println(1.45f + " --> " + String.format("%.1f", 1.45f)); System.out.println(1.55f + " --> " + String.format("%.1f", 1.55f));}
结果:1.45 --> 1.51.55 --> 1.5
大家看出问题来了吗,在python和java中四舍五入的结果跟我们想的不一样,而且python和java的四舍五入规则还不一样,这是为什么?
实际值 | 1.45 | 1.55 |
---|---|---|
python round | 1.4 | 1.6 |
java round | 1.5 | 1.5 |
二、猜想
针对上面的现象,肯定不能说编程语言有bug,那么编程语言为什么会出现这样的结果呢,我猜想原因可能是这样的。在计算机中,所有数据的存储全是0和1,这是常识。对于数字也不例外,整数的存储很好理解,就是二进制,比如1001,就代表9。对于小数的存储,一般用浮点数表示,浮点数的整数部分可以用二进制精确表示,但是小数部分,却无法用二进制精确表示,只能是一个无限接近的值。对于上面round的结果跟我们预想的不一样会不会是由于浮点数特有的误差引起的呢?比如1.45在计算机中可能存的是1.44999999,也可能存的是1.45000001。前者round保留一位小数的确就是1.4,而后者却是1.5。
三、验证
为了验证上面的猜想,我们先用java看看一组数四舍五入后的结果。
public static void main(String[] args) { float f[] = {1.05f, 1.15f, 1.25f, 1.35f, 1.45f, 1.55f, 1.65f, 1.75f, 1.85f, 1.95f}; for (float v : f) { String result = String.format("%.1f", v); System.out.println(v + " --> " + result); }}
结果:1.05 --> 1.0 不正确1.15 --> 1.1 不正确1.25 --> 1.31.35 --> 1.41.45 --> 1.51.55 --> 1.5 不正确1.65 --> 1.6 不正确1.75 --> 1.81.85 --> 1.91.95 --> 2.0
现在我们只需要知道这个小数在计算机中是怎么存储的,就可以验证我们的猜想了。如何将一个小数转换为浮点数算法比较复杂,为了节约篇幅,不在本文介绍。我们可以通过一个小工具进行转换。现行的浮点数算法采用的是IEEE 754,我们可以登录这个网页(https://www.h-schmidt.net/FloatConverter/IEEE754.html),利用上面提供的工具将小数转换为浮点数,看看我们所输入的值实际在计算机中存储的是什么样子。在工具里填入一个实际的值,下面的结果自动生成。
输入值 --> 计算机实际存储的值1.05 --> 1.04999995231628417968751.15 --> 1.149999976158142089843751.25 --> 1.251.35 --> 1.350000023841857910156251.45 --> 1.45000004768371582031251.55 --> 1.54999995231628417968751.65 --> 1.649999976158142089843751.75 --> 1.751.85 --> 1.850000023841857910156251.95 --> 1.9500000476837158203125
可以看到正好跟java吻合。1.05存储的是1.0499999523162841796875,将这个数四舍五入保留一位小数不正好就是1.0么。
接下来看看python中的情况
>>> l = [1.05, 1.15, 1.25, 1.35, 1.45, 1.55, 1.65, 1.75, 1.85, 1.95]>>> for v in l:... print(v, "-->", round(v,1)) ... 1.05 --> 1.11.15 --> 1.1 不正确1.25 --> 1.21.35 --> 1.41.45 --> 1.4 不正确1.55 --> 1.61.65 --> 1.6 不正确1.75 --> 1.81.85 --> 1.91.95 --> 1.9 不正确
可以看到python跟java又不太一样,难道说python使用的是另一种算法?猛然想起,浮点数还分单精度和双精度,java的float是单精度,python的小数会不会是双精度呢?我们先看看双精度的浮点数如何表示。上面的工具只提供了单精度的转换,我们再另外找一个工具(https://www.binaryconvert.com/result_double.html?decimal=049046057053)。下面贴出来双精度的转换结果。
输入值 --> 计算机实际存储的值1.05 --> 1.050000000000000044408920985011.15 --> 1.149999999999999911182158029991.25 --> 1.251.35 --> 1.350000000000000088817841970011.45 --> 1.449999999999999955591079014991.55 --> 1.550000000000000044408920985011.65 --> 1.649999999999999911182158029991.75 --> 1.751.85 --> 1.850000000000000088817841970011.95 --> 1.94999999999999995559107901499
可以看到双精度与python四舍五入的结果也正好吻合。
三、复核
到这里我其实很确信我的猜想是对的,但是为了进一步证明,我们还是要看官方的文档。
在python中我们可以通过以下代码,直接查看一个小数在计算机中存储的实际值,可以看到跟双精度浮点数完全一样。
>>> from decimal import Decimal>>> l = [1.05, 1.15, 1.25, 1.35, 1.45, 1.55, 1.65, 1.75, 1.85, 1.95]>>> for v in l:... print(v, "-->", Decimal.from_float(v)) ... 1.05 --> 1.05000000000000004440892098500626161694526672363281251.15 --> 1.1499999999999999111821580299874767661094665527343751.25 --> 1.251.35 --> 1.3500000000000000888178419700125232338905334472656251.45 --> 1.44999999999999995559107901499373838305473327636718751.55 --> 1.55000000000000004440892098500626161694526672363281251.65 --> 1.6499999999999999111821580299874767661094665527343751.75 --> 1.751.85 --> 1.8500000000000000888178419700125232338905334472656251.95 --> 1.9499999999999999555910790149937383830547332763671875
另外python的官方文档(https://python-reference.readthedocs.io/en/latest/docs/functions/round.html)里面也记录了一段话:“The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float”。
四、联想
既然编程语言有这些现象,那么我们常用的数据库也有这个问题吗?因为我所处的行业是金融行业,对数据非常敏感,需要精确到分,即0.01,差一分也是问题。
1. 首先查看excel
excel四舍五入正常
2. 其次查看oracle
SQL> create table t1 (id number, name varchar2(100));SQL> insert into t1 values (1.05, "1.05");SQL> insert into t1 values (1.15, "1.15");SQL> insert into t1 values (1.25, "1.25");SQL> insert into t1 values (1.35, "1.35");SQL> insert into t1 values (1.45, "1.45");SQL> insert into t1 values (1.55, "1.55");SQL> insert into t1 values (1.65, "1.65");SQL> insert into t1 values (1.75, "1.75");SQL> insert into t1 values (1.85, "1.85");SQL> insert into t1 values (1.95, "1.95");SQL> commit;SQL> select id, name, round(id, 1) from t1; ID NAME ROUND(ID,1)---------- ---------- ----------- 1.05 1.05 1.1 1.15 1.15 1.2 1.25 1.25 1.3 1.35 1.35 1.4 1.45 1.45 1.5 1.55 1.55 1.6 1.65 1.65 1.7 1.75 1.75 1.8 1.85 1.85 1.9 1.95 1.95 2
oracle四舍五入正常
3. 再看mysql
mysql的小数有几种类型,float,double,decimal。float和double就是前面说的单精度和双精度浮点数,decimal是定点数,本文只讨论浮点数,因此定点数不做解释,只拿来做对比。
(root@localhost)[hello]> create table t2 (id1 float(10, 2), id2 decimal(10, 2), name varchar(100));(root@localhost)[hello]> insert into t2 values (1.05, 1.05, "1.05");(root@localhost)[hello]> insert into t2 values (1.15, 1.15, "1.15");(root@localhost)[hello]> insert into t2 values (1.25, 1.25, "1.25");(root@localhost)[hello]> insert into t2 values (1.35, 1.35, "1.35");(root@localhost)[hello]> insert into t2 values (1.45, 1.45, "1.45");(root@localhost)[hello]> insert into t2 values (1.55, 1.55, "1.55");(root@localhost)[hello]> insert into t2 values (1.65, 1.65, "1.65");(root@localhost)[hello]> insert into t2 values (1.75, 1.75, "1.75");(root@localhost)[hello]> insert into t2 values (1.85, 1.85, "1.85");(root@localhost)[hello]> insert into t2 values (1.95, 1.95, "1.95");(root@localhost)[hello]> select id1, round(id1,1), id2, round(id2,2), name from t2;+------+--------------+------+--------------+------+| id1 | round(id1,1) | id2 | round(id2,2) | name |+------+--------------+------+--------------+------+| 1.05 | 1 | 1.05 | 1.05 | 1.05 || 1.15 | 1.1 | 1.15 | 1.15 | 1.15 || 1.25 | 1.2 | 1.25 | 1.25 | 1.25 || 1.35 | 1.4 | 1.35 | 1.35 | 1.35 || 1.45 | 1.5 | 1.45 | 1.45 | 1.45 || 1.55 | 1.5 | 1.55 | 1.55 | 1.55 || 1.65 | 1.6 | 1.65 | 1.65 | 1.65 || 1.75 | 1.8 | 1.75 | 1.75 | 1.75 || 1.85 | 1.9 | 1.85 | 1.85 | 1.85 || 1.95 | 2 | 1.95 | 1.95 | 1.95 |+------+--------------+------+--------------+------+
可以看到mysql的浮点数四舍五入有问题,定点数四舍五入正常。
4. 最后看下sqlite
sqlite> create table t3 (id real, name varchar(100));sqlite> insert into t3 values (1.05, "1.05");sqlite> insert into t3 values (1.15, "1.15");sqlite> insert into t3 values (1.25, "1.25");sqlite> insert into t3 values (1.35, "1.35");sqlite> insert into t3 values (1.45, "1.45");sqlite> insert into t3 values (1.55, "1.55");sqlite> insert into t3 values (1.65, "1.65");sqlite> insert into t3 values (1.75, "1.75");sqlite> insert into t3 values (1.85, "1.85");sqlite> insert into t3 values (1.95, "1.95");sqlite> select id, round(id,1), name from t3;1.05|1.1|1.051.15|1.1|1.151.25|1.3|1.251.35|1.4|1.351.45|1.4|1.451.55|1.6|1.551.65|1.6|1.651.75|1.8|1.751.85|1.9|1.851.95|1.9|1.95
可以看到sqlite四舍五入不正常,而且sqlite的小数采用的是双精度,sqlite的官方文档(https://www.sqlite.org/datatype3.html)也记录了:“REAL. The value is a floating point value, stored as an 8-byte IEEE floating point number.”
五、总结
- 当小数的四舍五入最后一位是5的时候,是否进1,要看这个小数实际在计算机中存储的值。
- 对于数据库来说,oracle没有这方面的问题,mysql尽量使用定点数,sqlite暂时没有定点数,临时解决办法可以采用整数存储,在计算的时候将其除以对应的倍数。
- 至于编程语言的四舍五入问题,大家自己想办法吧。
本文参考:https://www.sqlite.org/datatype3.htmlhttps://docs.python.org/3/tutorial/floatingpoint.html#tut-fp-issueshttps://www.h-schmidt.net/FloatConverter/IEEE754.htmlhttps://www.binaryconvert.com/result_double.html?decimal=049046052053
关键词:
-
详解 APISIX Lua 动态调试插件 inspect
作者罗锦华,API7 ai技术专家 技术工程师,开源项目pgcat,lua-resty-ffi,lua-resty-inspect的作者。...
来源: -
四舍五入 - 逼疯全世界的开发者
一、说明我们先来看一组例子【PYTHON】Python3 6 10(default,Apr62021,21:58:27)[GCC4 8 520150623(RedHat4
来源: 详解 APISIX Lua 动态调试插件 inspect
四舍五入 - 逼疯全世界的开发者
全球热议:NTP网络校时服务器(北斗GPS校时器)在地铁内网系统中的应用
【新视野】半导体设备板块快速走高,金海通涨停
年内公募新基金发行突破2800亿元 债券型基金占比超六成
《Redfall》引发粉丝强烈不满
环球焦点!《名侦探柯南》新剧场版上映 柯哀亲了登顶热搜!新兰CP党表示要弃剧
上海车展全球首发 比亚迪B级纯电SUV官宣:命名“宋L”
焦点快看:女子团购住民宿 结果住到自己家的房子 网友:王思聪有这体验
【全球快播报】荣耀Magic5成旗舰标杆!赵明:从来没有友商敢挑战它的续航、屏幕
环球速看:中标 | 含塔筒3200-3438元/kW!海装、金风、电气风电预中标山东能源渤中海上风电项目
Spring Boot 接口加解密,新姿势来了!
全球微头条丨苹果iPhone 15 Pro机型再曝光:依然实体音量键 静音拨片改为Action按钮
苹果和安卓折腾了5年 愣是干不过微信小程序
抢先看!迪士尼更新真人版《小美人鱼》预告:黑小美人鱼海下展唯美瞬间
世界看热讯:标致408保价期内降价遭集体投诉 车主:4S店称地方补贴不算降价
【全球聚看点】现场办公面对面听意见解难题 西固区定制化服务助企纾困
天天微动态丨【读财报】消费主题基金年报透视:2022年净值普跌 全年共亏逾280亿
每日热门:清华、北大在校生五一淄博免费游”?官方回应:消息属实
快讯:巴奴“天价土豆”换名后重新上架:多地售价18元一份 北京上海22元
云南16岁女孩载21岁女孩无证驾驶摩托车身亡 生前网友曾劝危险
天天观热点:张朝阳建议年轻人多做饭、早上6点就起:外卖都是垃圾!王石唱反调
全球微头条丨问界M5系列华为高阶智驾版来了:不惧鬼探头 实现泊车“自由”
读SQL进阶教程笔记11_关系数据库基础
天天实时:从消博会透视中国消费活力
每日快播:美股财报季拉开前夕美银泼冷水:美企盈利将从“糟糕”走向“更糟”
【天天报资讯】沙特俄罗斯产油国联手减产 国际油价飙升:美国难受 补充战略石油储备
【世界新要闻】女性同事过生日送什么花
环球快讯:ASP.NET Core MVC 从入门到精通之路由
全国4亿人集体“吃土”?沙尘暴持续下该如何保命
当前动态:都江堰商业街现多只红嘴相思鸟尸体:专家判断系撞玻璃死亡
世界聚焦:菜花蛇、眼镜王蛇、王蛇:谁才真正的毒蛇之王?
【播资讯】国产科幻FPS《边境》Steam褒贬不一:UI稀烂 错误频出
天天速递!微星RTX 4070魔龙深度测试:老黄的刀法太精准了!
环球时讯:老两口搬家花4万,这么多年过去了,搜索引擎“搬家刺客”依然存在?
废弃矿井地下水中多环芳烃的降解与迁移机理
1811 E Living Sequence 两种解法
ChatGPT进化,人性消失,究竟是好是坏?
每日聚焦:第137篇:重学ES6模块化
天天热门:容器操作,应用部署,迁移与备份,Dockerfile
焦点快播:20亿的交易被华为反对 没任何可能合作 诺基亚坚持要卖鼎桥母公司股权
黄仁勋主持 台北电脑展NVIDIA发布会敲定!RTX 4060 Ti/4060要来了
焦点消息!《塞尔达传说:王国之泪》终极预告发布:短发公主绝美 盖侬真身登场
天天微资讯!仿真-定向耦合器
今日报丨java 处理常量字符串过长 & springboot 项目读取 resouces 文件夹下的文件内容
ORACLE还原恢复启动时数据库报ORA-00704, ORA-00604, ORA-00904
世界速读:linux环境下安装Docker
世界关注:百度松果菁英班--oj赛(第二次)
面试题百日百刷-kafka篇(三)
速讯:AMD发布RDNA3架构Radeon Pro W7000系列专业显卡:AI性能提升2.7倍、满血3999美元
天天播报:比“500万内最好的SUV”更好!魏牌蓝山上市:27.38万元起
当前聚焦:纯白机身美翻了!影驰RTX 4070星曜OC图赏
为什么今年沙尘暴突然这么严重?40多年植树造林白干了?
环球播报:79888元!大疆Inspire 3发布:全画幅8K电影无人机
葵花药业实控人杀妻案4年后:两女儿接班
【世界独家】大疆悟3曝光:吊着一台摄像机 支持8K 75fps视频录制
每年减少6吨补给 我国空间站氧气100%再生:已达世界领先水平
环球今日报丨强力透气 森马旗下棉致男士冰丝休闲裤34.5元
当前热点-夜莺监控V6初探
每日关注!双碳ETF: 招商基金管理有限公司关于旗下基金投资关联方承销证券的公告
热讯:苹果寻觅中国制造之外新选择:正推动将MacBook转到泰国组装
今日看点:安卓之光未发先火!超1000位米粉下单小米13 Ultra
巴菲特对苹果推崇至极!出1万美元买不走你的iPhone但能买走你的福特车
当前快报:新能源厂商销量榜 长城、五菱、小鹏汽车全部接近腰斩
环球焦点!飞腾CPU走出国门 非洲电厂将用上“中国芯”
每日关注!Java GC基础知识
天天微资讯!KMP(字符串匹配算法)
焦点快报!银行发现7位数存款5年未动急寻人:联系电话是空号
每日热文:RTX 4090与RTX 3080 Ti移动旗舰显卡大PK:没想到性能差距巨大
自动驾驶为啥这么难!技术问题?还是方向错了?
每日视点!T-1000未来或成真!《终结者》之父:AI太强可以造成世界末日
焦点消息!被狗抓破是否会得狂犬病上热搜:博主“科普”网友纷纷不认账
当前热门:三分钟完成静态网站托管
每日视讯:ChatGPT吃我饭,还要掀我碗
cookies、sessionStorage与localStorage在Vue中的使用
有关拦截器拦截指定的url并作出处理返回数据
环球热点评!皇马这批人如果拿到6个欧冠,历史地位也没变化?受害者只有C罗?
环球短讯!华为5G回来了?余承东体验AITO问界M5智驾版:重大更新引人遐想
疯狂白菜价 DDR5正式跌破百元 国产枭鲸8GB内存只要99.9元
全球观热点:表现持平GPT-4!知乎“知海图AI”投入内测
《终结者》之父卡梅隆:AI太厉害将能造成世界末日
环球速读:云南万人大型泼水现场互送祝福:欢度泼水节
全球即时:全栈混合云综合架构方案研究和落地
环球观热点:详解GaussDB(DWS)的query_band负载识别与应用
快看:零基础快速入门WebRTC:基本概念、关键技术、与WebSocket的区别等
【世界时快讯】Java集成工作流审批机制,多个项目实际运用优化版本(干货)
世界快播:Natasha入门(一)
环球观热点:恒生指数13日收涨0.13% 生物医药行业领涨
焦点报道:许昌市东城区:普法宣传进校园 精心护苗筑平安
【全球时快讯】NVIDIA最新调查:80%的RTX 40系显卡用户会启用光追和DLSS
【天天播资讯】侧透玻璃机箱!微星海皇戟AS台式机上新:i7+RTX 4070
世界新消息丨同跑硬件杀手《赛博朋克2077》 RTX 4080与RTX 4090有多大差距?
世界今日讯!Win掌机不用指头戳屏幕了!Win12有望加入“掌机模式”
世界微动态丨下周一!国内油价预计涨0.44元/升 或创今年最大涨幅
尼尔森IQ:中国消费市场回暖 绿色变革赋能品牌可持续增长
天天速读:MegEngine 使用小技巧:使用 Optimizer 优化参数
每日动态!图片压缩&视频截取
全球观速讯丨Mac | iOS | Windows:安装Stable diffusion教程
世界短讯!科创板收盘播报:科创50指数低开低走跌2.59% 软件服务及半导体股跌幅居前
世界热讯:简历上写QQ邮箱会掉分吗引热议 网友:邮箱也分三六九等?