c++ double保留两位小数_【新手程序员日经问题FAQ】【01】“编程时小数型的表现不符合直觉?”

写在前面

因为经常收到一些新手程序员的邀请,并且问题重复率很高。经过一番思考,我决定在空闲时间写一些相关的文章,以后遇到相关的问题可以直接发文章链接,从而高效的解决问题,另外希望也起到一些科普的作用。这系列的文章中我刻意的没有深入到问题的细节中,而是以一个“大概差不多”的标准来写作,让解答尽量短、新手向以及不枯燥。但这样必然会导致内容的精细度下降,比如说不会涉及到极端情况,或者有一些数据不够精确等等,毕竟不可兼得。同时也不希望有人因此来杠,当然,欢迎友好的讨论、补充和纠错。


这篇文章中整合了关于小数型(浮点数)精度相关的日经问题。

通用知识

在大部分编程语言中,小数型的存储都遵循于IEEE754二进制浮点数算术标准[1],通常我们称它们为浮点数。常用的浮点数类型有floatdouble,前者占用4字节空间,后者占用8字节,当然后者的表示范围和精度都更高。(在比较简单的语言中,可能只提供一种浮点型。)

是的,当然有表示范围。毕竟就这么几个字节,能存多少位数嘛。整数范围本文不谈,小数范围,毕竟数学上小数是无限多的,比如0~1这个范围内,有0.1还有0.01还有0.001……还有0.00000000000001。总之大约来说,float大约能装到小数点后五六七位差不多,double能到十五六七位


“为什么我的运算结果有微小的偏差?为什么两个数字比较的结果反直觉?”

先说第一个问题,通常是在运算之后,发现结果比预期差了一点点。比如下例中0.1+0.2=0.30000000000000004

C:Usersmoe001>python
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> 0.1+0.2
0.30000000000000004

从结果来看,比预期的多了0.00000000000000004,该问题源于两个部分。

1.进制引起的循环小数截断

就拿0.1来举例,十进制下干干净净,它就是0.1。但计算机内部都是二进制,在二进制下,它是一个循环小数——0.000 1100 1100 ... 1100,后面1100部分无限循环。没什么奇怪的,想象如果有之所谓的π进制,那我们现在所有的整数转换过去,不都是无限小数。

而浮点型存储这样的数,当然只能精确到某一位,把后面的截断掉,这导致我们输入的0.1,实际上计算机内部只能是一个近似值,准确来说,是0.1000000000000000055511151231257827021181583404541015625[2]

可以看到近似值在一定位数(有效精度)之后,出现了一些很乱的数字,这些数字通常情况下没什么影响,毕竟我们知道这东西有效位数,后面的截断不要,只保留十七位小数,0.10000000000000000着实还是0.1

2.运算引起的误差放大

上一节最后提到了截断掉“乱码区”,看上去是个不错的办法。但这些数字进行计算的时候,“乱码区”却是同样参与计算的,这留下了隐患。详细分析最初的例子:

  1. 0.1的近似值是0.1000000000000000055511151231257827021181583404541015625
  2. 0.2的近似值是0.200000000000000011102230246251565404236316680908203125
  3. 其之和的精确值0.3000000000000000166533453693773481063544750213623046875
  4. 如同0.1没法精确表示,这个数字虽然很长,但是也没他的精确值
  5. 取其近似值为0.3000000000000000444089209850062616169452667236328125

最终……保留十七位之后的结果就成了0.30000000000000004 。事情就是这么个事情,为什么说是误差放大呢,是因为两个“乱码区”相加之后,使得运算结果离0.3更远了,不然也许能取到一个更接近0.3的近似值。

然后说到第二个问题,懂了上面所说的,其实很简单,一笔带过。

一个类似的例子:

C:Usersmoe001>python
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> 0.1+0.2==0.3
False

上节提到0.1+0.2的结果为0.3000000000000000444089209850062616169452667236328125,而0.3 的近似值,是0.299999999999999988897769753748434595763683319091796875,因其更接近0.3。最终,这两个数自然是不相等的。所以,浮点型不要去判断相等或者不相等,通常的做法是判断两个数的差值是否小于某个阈值,比如说0.001,是的话就认为相等。

参考

  1. ^IEEE 754 https://en.wikipedia.org/wiki/IEEE_754
  2. ^如果深究的话,这个数来自1100110011001100110011001100110011001100110011001101(二进制)除以2的55次方,是的,浮点型内部就是这样存放数字的。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值