1.03 - 0.42 = ?

本文探讨了在C/C++、Java和Python中1.03-0.42运算结果为0.6100000000000001而非预期的0.61的原因。解释了这是由于这些语言遵循IEEE-754标准来存储浮点数,并受到计算机处理实数时的离散模拟限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最初发表在这里

 

0.61。一定确定以及肯定?看看计算机是怎样回答的:

一号选手C/C++的答案是:printf("%5.20f", 1.03-0.42);  output = 0.61000000000000010000
 二号选手Java的答案是:System.out.printf("%5.20f", 1.03-0.42) ; output = 0.61000000000000010000
三号选手Python的答案是:print "%5.20f"%(1.03-0.42); output = 0.61000000000000010000

     三名选手竟然异口同声说答案是0.61000000000000010000,而不是我们小学生都可以一定确定以及肯定的答案0.61,为何呢?原来,这三种(还有其它更多的)语言都是按照 IEEE-754 标准来规定浮点数的存储格式的。由于师出同门,得到相同的答案也就不足为奇了。令人不解的是它们得到的结果明摆着是错误的,我们为什么还要计算机遵守这个规定呢?呵呵,没办法,由于当前我们使用的计算机都是遵循冯.诺依曼体系结构的,只能处理离散数据,而且在计算机内部数据是以二进制的形式存放的,因此,对于实数这样的连续概念,我们只能通过离散量来模拟。这跟用多边形来模拟圆的道理是一样的,如果多边形的边越多,多边形跟圆的切点就越多,也就意味着该多边形与圆越相似,当边数趋于无穷时,多边形也就跟圆重合了。也就是说,我们只有通过无穷这个概念使得离散量可以精确地模拟出连续量,但是,在计算机科学中,我们是不能表示出无穷这个概念的。正是这个原因,用计算机来表示一个实数,在大多数情况下得到的都只是一个近似的结果。
    http://babbage.cs.qc.edu/courses/cs341/IEEE-754.html对IEEE-754做了一个很好的描述,大家可以参考一下。总的来说,在IEEE-754中,浮点数分为三个部分(以32位浮点数为例):
    1、符号位,决定该浮点数的正负(1位)
    2、指数,决定浮点数的 大小范围(8位)
    3、尾数,决定浮点数的表示精度(23位,由于使用规范化表示,最高位为1,因此可以表示24位的精度)。
    可以看出来,尾数的位数越多,我们所能精确表示的实数就越多(在多边形和圆的例子中就是两者的切点更多了)。因此,double类型的数据所能精确表示的实数个数要远远多于float类型的数据,其代价就是多用一倍的存储空间。但是,虽然这样,对于实数这个无穷无尽的庞然大物来说,double也是无能为力,还是有很多数字double类型都无法精确表示出来,12345.691234566778就是这样的一个例子。
     因此,如果我们要求精确的答案,就要避免使用float和double类型的数据,特别是在货币计算方面,这一点在Effective Java item 31中有详细的描述。此外,这也解释了为什么在判断两个浮点数是否相等时不能直接用相等符号进行比较,而要通过跟一个表示特定精度的数据进行比较得知。

if( 1.03-0.42 == 0.61){   
     System.out.println("Aha!");
  }else{
    System.out.println("Huh?");
  }
output = Huh?

if( (1.03-0.42)- 0.61 < 10e-6){   
     System.out.println("Aha!");
  }else{
    System.out.println("Huh?");
  }
output = Aha!

 

三类样本如下: 类别1:(-0.42,0.58,0.089)、(0.27,-0.45,-0.087)、(-0.39,0.17,-0.901)、(-0.13,0.31,0.61)、(-0.41,0.12,0.81)、(0.16,0.09,0.61)、(-0.23,0.15,0.057)、(0.27,0.3,0.87)、(-0.16,-0.07,0.21)、(-0.04,-0.17,0.81) 类别2:(-0.72,-0.82,-0.69)、(-1.47,-1.38,-0.12)、(0.18,-0.11,-0.77)、(-0.11,-0.3,-0.22)、(-0.46,-0.03,-0.22)、(-0.08,-0.23,-0.11)、(0.31,-1.08,-0.32)、(0.0,-0.13,-0.34)、(-0.43,-0.52,0.65)、(0.09,-0.21,0.05) 类别3:(1.22,1.27,0.89)、(0.69,1.27,0.99)、(1.03,0.94,0.55)、(1.16,1.03,0.64)、(0.51,0.39,-0.49)、(1.19,0.79,-0.03)、(0.49,0.99,0.91)、(0.99,0.87,1.18)、(0.59,0.63,0.27)、(1.0,0.61,0.68) 根据Fisher线性判决的原理,我们需要进行以下步骤: 1. 计算每一类样本的均值向量和协方差矩阵 2. 计算总体均值向量和总体协方差矩阵 3. 计算投影向量w 4. 对测试样本进行分类 代码如下: import numpy as np import matplotlib.pyplot as plt # 定义三类样本 class1 = np.array([[-0.42,0.58,0.089],[0.27,-0.45,-0.087],[-0.39,0.17,-0.901],[-0.13,0.31,0.61],[-0.41,0.12,0.81],[0.16,0.09,0.61],[-0.23,0.15,0.057],[0.27,0.3,0.87],[-0.16,-0.07,0.21],[-0.04,-0.17,0.81]]) class2 = np.array([[-0.72,-0.82,-0.69],[-1.47,-1.38,-0.12],[0.18,-0.11,-0.77],[-0.11,-0.3,-0.22],[-0.46,-0.03,-0.22],[-0.08,-0.23,-0.11],[0.31,-1.08,-0.32],[0.0,-0.13,-0.34],[-0.43,-0.52,0.65],[0.09,-0.21,0.05]]) class3 = np.array([[1.22,1.27,0.89],[0.69,1.27,0.99],[1.03,0.94,0.55],[1.16,1.03,0.64],[0.51,0.39,-0.49],[1.19,0.79,-0.03],[0.49,0.99,0.91],[0.99,0.87,1.18],[0.59,0.63,0.27],[1.0,0.61,0.68]]) # 计算每一类样本的均值向量和协方差矩阵 mean_class1 = np.mean(class1, axis=0) mean_class2 = np.mean(class2, axis=0) mean_class3 = np.mean(class3, axis=0) cov_class1 = np.cov(class1.T) cov_class2 = np.cov(class2.T) cov_class3 = np.cov(class3.T) # 计算总体均值向量和总体协方差矩阵 total_mean = np.mean(np.concatenate((class1, class2, class3), axis=0), axis=0) total_cov = np.cov(np.concatenate((class1, class2, class3), axis=0).T) # 计算投影向量w Sw = cov_class1 + cov_class2 + cov_class3 w = np.dot(np.linalg.inv(Sw), (mean_class1 - mean_class2 + mean_class3)) # 对测试样本进行分类 x1 = np.array([-0.7, 0.58, 0.089]) x2 = np.array([0.047, -0.4, 1.04]) g1 = np.dot(w.T, x1) - np.dot(w.T, total_mean) g2 = np.dot(w.T, x2) - np.dot(w.T, total_mean) if g1 > g2: print("x1属于类别1") else: print("x1属于类别2") if g2 > g1: print("x2属于类别2") else: print("x2属于类别1") # 画出图像 plt.scatter(class1[:, 0], class1[:, 1], color='r', label='class1') plt.scatter(class2[:, 0], class2[:, 1], color='b', label='class2') plt.scatter(class3[:, 0], class3[:, 1], color='g', label='class3') plt.plot([-w[1], w[1]], [w[0], -w[0]], color='k', label='Fisher') plt.legend() plt.show()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值