浮点数为什么会有误差?
static void Main(string[] args)
{
float a = 2.9f;
double b = 1.8;
Console.WriteLine(a-b);
Console.ReadLine();
}
输出结果为1.10000009536743
我觉得从计算机的存储原理来解释会好一些,由于计算机只能识别0和1,所以小数也不例外的必须按照二进制来进行储存。
首先我们得清楚小数转换为二进制的方法:对小数点以后的数乘以2,取结果的整数部分(非1即0),然后再用小数部分再乘以2,再取结果的整数部分……以此类推,直到小数部分为0,随后正序排列。
演示: 0.125 ×2=0.25 .......................0
0.25×2=0.5.............................0
0.5×2=1.0...............................1
即 0.125的二进制表示为小数部分为0.001
为什么0.1不能被二进制精确的表示出来呢?
0.1×2=0.2 ......................0
0.2×2=0.4 ......................0
0.4×2=0.8 ......................0
0.8×2=1.6.......................1
0.6×2=1.2.......................1
0.2×2=0.4.......................0
.....
于是它的二进制形成了一系列的无限循环小数。
而计算机中存储的位数是有限的,因此当变量存储不了后面的二进制小数时,再转换回十进制就产生误差了。
0.9 十进制转换为二进制是:
0.111001100110011001100…无限循环所以0.9的表示受精度所限,精度以后的就被忽略了
float时: 十进制保存7位置,二进制保存23位
0.111001100110011001100…转换回十进制就是:
0.89999998double时: 十进制保存15 ~ 16位,二进制保存52位
0.111001100110011001100…转换回十进制就是:
0.90000000000000002
在使用C#编程的过程中,我暂未发现float受精度所限的情况,貌似会自动取整;但是用double类型时常常会出现精度丢失的问题(除了2的n次以及浮点数加减乘除整数可以准确表示)。
我们应该如何解决精度丢失呢?
这里我提供两种我较常用的解决办法:1.强制类型转换为float。2.用{0:f1}占位符方法限制小数点后位数显示。
1.强制类型转换为float,这个较好理解,以下代码示例:
float a = 2.9f;
double b = 1.8;
Console.WriteLine((float)(a-b));
2.用{0:f1}占位符方法限制小数点后位数显示,这个方法,也较好理解,f1就保留一位小数,f2保留两位,以下代码示例:
float a = 2.9f;
double b = 1.8;
Console.WriteLine("a-b的值是{0:f1}",a-b);
3.引入Math.Round(a,b)方法(a为变量,b为保留的小数位数),这个方法用于四舍五入,以下代码示例:
float a = 2.9f;
double b = 1.8;
Console.WriteLine(Math.Round(a - b,3));