浮点运算结果为什么会出现误差

       在实际编程中,经常会用到浮点运算,大家可能会发现其结果出现误差,与实际期待值不一样。为什么。 其根本原因是计算机所使用二进制01代码无法准确表示某些带小数位的十进制数据。下面我们来分析下。我们知道将一个十进制数值转换为二进制数值。

在实际编程中,经常会用到浮点运算,大家可能会发现其结果出现误差,与实际期待值不一样。如下C#代码:

  float a = 0.65f;

  float b = 0.6f;

  float c = a - b;

  此时c为多少?

  0.05?错误!

  此时c为0.0499999523!

  为什么

  其根本原因是计算机所使用二进制01代码无法准确表示某些带小数位的十进制数据。

  下面我们来分析下:

  我们知道将一个十进制数值转换为二进制数值,需要通过下面的计算方法:

  1. 整数部分:连续用该整数除以2,取余数,然后商再除以2,直到商等于0为止。然后把得到的各个余数按相反的顺序排列。简称"除2取余法"。

  2. 小数部分:十进制小数转换为二进制小数,采用"乘2取整,顺序排列"法。用2乘以十进制小数,将得到的整数部分取出,再用2乘余下的小数部分,然后再将积 的整数部分取出,如此进行,直到积中的小数部分为0或者达到所要求的精度为止。然后把取出的整数部分按顺序排列起来,即先取出的整数部分作为二进制小数的高位,后取出的整数部分作为低位有效位。简称"乘2取整法"。

  3. 含有小数的十进制数转换成二进制,整数、小数部分分别进行转换,然后相加。

  例如:将十进制数值25.75转换为二进制数值,步骤如下:

  25(整数部分)

  25/2=12......1

  12/2=6.......0

  6/2=3......0

  3/2=1......1

  1/2=0......1

  (25) 10=(11001) 2

  0.75(小数部分)

  0.75*2=1.5......1

  0.5*2=1......1

  (0.75) 10=(0.11) 2

  (25.75) 10=(11001) 2+(0.11) 2=(11001.11) 2

  按照上述方法,我们将0.65及0.6转换为二进制代码:

  (0.65)10 =(0.101001100110011001100110011001100110011......) 2

  (0.6) 10 =(0.10011001100110011001100110011001100110011......) 2

  后面的省略号表示已经算不完了,后面在无限重复 0011 这段二进制数值。

  文章开始部分,我们用的float类型,下面我们来看看float类型是否能存储上面转换出的二进制代码。

  目前计算机上存储浮点数值是按照IEEE(电气和电子工程师协会)754浮点存储格式标准来存储的。

  IEEE单精度浮点格式共32位,包含三个构成字段:23位小数f,8位偏置指数e,1位符号s。将这些字段连续存放在一个32位字里,并对其进行编码。其中0:22位包含23位的小数f; 23:30位包含8位指数e;第31位包含符号s。如下图所示:

 

 

                                       

 

 

  也就是说上面将0.65及0.5转换出的二进制代码,我们只能存储23位,即使数据类型为double,也只能存储52位,这样大家便能看出问题出现的原因了。

  截取的二进制代码已无法正确表示0.65及0.5,根据这个二进制代码肯定无法正确得到结果0.05。

  如何解决这个问题?知道其根本原因后,我们知道是无法从根本上解决这个问题的,但我们可以有一些曲线救国的方法,下面列举几个:

  1. 因为二进制数值可以准确表示整数(可以使用整数转换为二进制方法验证下),所以可以将小数乘以10或100等变成整数,然后做运算,最后再通过除以10或100等获得结果;

  2. 通过截取结果的有效小数位数等,来取得最好的近似结果,然后在做处理。

  3. 对于可以用有限长度的二进制数值表示的十进制数值,可以使用存储位数大于其长度的数据类型。

  解决方案正在补充中……,若各位有什么好的方法也可以提出来!

  以上解决方案需要按照使用的实际情况来决定使用哪种方法。

 


我们团队有着十几年的期货程序化交易算法与软件研发经验,基于C++ Qt技术研发了具有自主知识产权的期货智能程序化交易一体化系统平台,该平台封装了二百多个量化指标,具有低时延、高性能、小滑点、可定制和跨平台的特点。团队致力于将人工智能技术与传统的程序化交易技术相结合为客户提供灵活可定制的期货智能程序化交易服务和产品。

       

<think>嗯,用户问为什么浮点类型的小数转成二进制会有误差。这个问题我记得和计算机的二进制表示方式有关,特别是浮点数的存储机制。比如,像0.1这样的十进制小数,在二进制里其实是无限循环的,对吧?那计算机只能用有限的位数来存储,所以就会舍入,导致误差。 首先,我应该解释IEEE 754标准,因为这是浮点数的基础。比如单精度有32位,双精度64位。这些位数里分为符号位、指数部分和尾数部分。尾数的位数有限,所以无法精确表示所有小数。 然后,举例子可能更容易理解。比如0.1在十进制转二进制的时候,会变成0.0001100110011...无限循环。这时候,计算机只能保存到一定位数,后面的就被截断了,所以会有精度损失。 另外,可以提到十进制和二进制分数之间的转换问题。不是所有十进制小数都能用二进制有限表示,只有分母是2的幂次的分数才行。比如0.5是1/2,可以准确表示,但0.1的分母是10,分解后会有5的因子,导致二进制无法精确表示。 还要说明浮点数的舍入误差是普遍存在的,不仅仅是Python的问题,而是所有使用IEEE 754标准的编程语言都会遇到。可以举Python里的例子,比如0.1 + 0.2不等于0.3,而是接近0.30000000000000004。 最后,可能需要给出解决方案,比如使用decimal模块来处理高精度的小数运算,或者用分数表示。不过用户的问题主要是问原因,所以这部分可以简要带过,但作为补充还是有帮助的。 现在要确保回答的结构清晰,先解释原因,再举例说明,最后可能的解决办法。相关问题的话,可能会涉及IEEE 754的结构、如何避免误差、Python中的处理模块,以及十进制转二进制的其他例子。</think><< 浮点类型的小数转化为二进制出现误差的原因主要与计算机的二进制浮点数表示方式和有限的存储空间有关。以下是详细解释: 1. **二进制浮点数的有限精度**: 计算机使用IEEE 754标准表示浮点数(如`float`类型)。例如,单精度浮点数(32位)和双精度浮点数(64位)的尾数部分(mantissa)只能存储有限位数。无法精确表示无限循环或超出尾数容量的二进制小数。 ```python # 示例:0.1的二进制表示是无限循环的 0.1 → 二进制 0.0001100110011001100110011001100110011... ``` 2. **十进制与二进制分数的转换问题**: 不是所有十进制小数都有有限的二进制表示。例如,0.1(十进制)对应的二进制是无限循环小数,但计算机只能截断到有限位,导致精度丢失。 ```python # Python中的精度误差示例 print(0.1 + 0.2 == 0.3) # 输出:False print(0.1 + 0.2) # 输出:0.30000000000000004 ``` 3. **IEEE 754的舍入规则**: 存储时会按最近偶数(Round to Nearest)规则舍入,进一步引入误差。例如,无法精确表示的数值会被近似为最接近的可表示值。 **解决方法**: - 使用`decimal`模块进行高精度十进制运算: ```python from decimal import Decimal print(Decimal('0.1') + Decimal('0.2') == Decimal('0.3')) # 输出:True ``` - 使用分数(`fractions`模块)或字符串表示避免二进制转换。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值