代码改变世界

关于int、float、double一些知识的整理

2011-06-16 21:54  lujiao_cs  阅读(1626)  评论(0编辑  收藏  举报

关于 int.MaxValue:

首先看一个小程序:

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 int i = int.MaxValue;
6 Console.WriteLine("int.MaxValue:" + int.MaxValue);
7 i++;
8 Console.WriteLine("int.MaxValue + 1:" + i);
9 Console.ReadKey();
10 }
11 }

输出为:

int.MaxValue:2147483647

int.MaxValue + 1:-2147483648

 

为什么呢?

在Int32.cs 内部 MaxValue 定义为一个常量:

public const int MaxValue = 0x7fffffff ; 就是十进制的 2147483647

 

int.MaxValue+1之后,i的值为:0X80000000。由于计算机内存里面保存的都是二进制的补码形式,则对应的十进制数即为:-2147483648。注意C#内部并没有对int做溢出检查。因此不会出现异常,但是加一个checked就可以了。

 

Int, float, double 之间的相互转换:

Int 在.NET中,int一个有符号32位整数 ,无论处理器是32位还是64。它的.NET框架类型是System.Int32                                                      

Float单精度浮点数,32位长度,1位实数符号位(原码表示),1位指数符号位,7位指数位(此八位为移码表示)以及23位数据位(补码表示),在.net中又称为Single。

Double: 64位长度的双精度浮点数,1位符号位,11位指数位,52位数据位。

三者的关系:int可以隐式转换成float和double;float只能强制转换成int,但是可以隐式转换成double;double只能强制转换成float和int。

 

将一个float型转化为内存存储格式的步骤为:

(1)先将这个实数的绝对值化为二进制格式,注意实数的整数部分和小数部分的二进制方法在上面已经探讨过了。 
(2)将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边。 
(3)从小数点右边第一位开始数出二十三位数字放入第22到第0位。 
(4)如果实数是正的,则在第31位放入“0”,否则放入“1”。 
(5)如果n 是左移得到的,说明指数是正的,第30位放入“1”。如果n是右移得到的或n=0,则第30位放入“0”。 
(6)如果n是左移得到的,则将n减去1后化为二进制,并在左边加“0”补足七位,放入第29到第23位。如果n是右移得到的或n=0,则将n化为二进制后在左边加“0”补足七位,再各位求反,再放入第29到第23位。

 

举例说明: 11.9的内存存储格式

(1) 将11.9化为二进制后大约是"1011. 1110011001100110011001100..."。

(2) 将小数点左移三位到第一个有效位右侧:"1.011 11100110011001100110 "。 保证有效位数24位,右侧多余的截取(误差在这里产生了 )。

(3) 这已经有了二十四位有效数字,将最左边一位“1”去掉,得到“ 011 11100110011001100110 ”共23bit。将它放入float存储结构的第22到第0位。

(4) 因为11.9是正数,因此在第31位实数符号位放入“0”。

(5) 由于我们把小数点左移,因此在第30位指数符号位放入“1”。

(6) 因为我们是把小数点左移3位,因此将3减去1得2,化为二进制,并补足7位得到0000010,放入第29到第23位。

   最后表示11.9为: 0 1 0000010 011 11100110011001100110

 

将一个内存存储的float二进制格式转化为十进制的步骤: 
(1)将第22位到第0位的二进制数写出来,在最左边补一位“1”,得到二十四位有效数字。将小数点点在最左边那个“1”的右边。 
(2)取出第29到第23位所表示的值n。当30位是“0”时将n各位求反。当30位是“1”时将n增1。 
(3)将小数点左移n位(当30位是“0”时)或右移n位(当30位是“1”时),得到一个二进制表示的实数。 
(4)将这个二进制实数化为十进制,并根据第31位是“0”还是“1”加上正号或负号即可。 

 

问题一:

  1. int i = Int32.MaxValue;

2. float f = i;

3. int j = (int)f;

4. bool b = i == j; //false 

 

为什么结果为false呢?

int.MaxValue 大小为 2^31-1,写成二进制补码形式就是01111…(31个1),这个数,在表示成float的科学计数法的时候,将会写成+0.1111…(23个1)*2^31(因为有8为是指数位),对于那31个1,里面的最后8个,被float无情的抛弃了,此隐式转换造成了数据丢失。因此,再将这个float强制转换回int的时候,对应的int的二进制补码表示已经变成了0111…(23个1)00000000(最后八位用 0 填充),这个数与最初的那个int相差了255,所以造成了不相等。

当然,b 也会有true的情况。对于一个int,把它写成二进制形式之后,成为了个一32个长度的0、1的排列,对于这个排列,只要第一个1与最后一个1之前的间距,不超过23,那么它转换成float再转换回来,两个值就会相等。这个问题是与大小无关的,而且这个集合在int这个全集下并不连续。

 

问题二:

  1. double d = 0.6;

  2. float f = (float)d;

  3. double d2 = f;

4. bool b = d == d2; //false

 

为什么结果还是false?

首先,需要将0.6转换为二进制:结果是0.10011001……(1001循环)。这是一个无限循环小数。计算机在存储它的时候,只能存储近似值。这样的话由于float保存23位,而double保存52位,就造成了double转化成float的时候,丢失掉了一定的数据,再转换回去的时候,那些丢掉的值被补成了0,因此这个后来的double和从前的double值已经不再一样了。

同样,b 也会有true的情况。这个和"问题一"是一样的,我们还需要考虑double比float多了3位的指数位。

B为false,那d的值是变大了还是变小了呢?计算机再计算二进制小数也挺简单,就是0舍1入。对于float,要截断成为23位,假如卡在24位上的是1,那么就会造成进位,这样的话,存起来的值就比真正的十进制值大了,如果是0,就舍去,那么存起来的值就比真正的十进制值小了。因此,这可以合理的解释一个问题,就是0.6d转换成float再转换回double,它的值是0.60000002384185791,这个值是比0.6大的,原因就是0.6的二进制科学计数法表示,第24位是1,造成了进位。

 

注意:在需要精确答案的地方,要避免使用floatdouble,对于货币计算,要使用int、long或Decimal。 

浮点十进制值可能丢失精度通常是因为没有完全相同的二进制表示形式。 这是 CPU 所采用的浮点数据表示形式的副作用。 为此,可能会经历一些精度丢失,并且一些浮点运算可能会产生意外的结果。

 

参考:

http://hi.baidu.com/zengzhaonong/blog/item/1780a5a1cf959a884610646c.html 

http://www.cnblogs.com/wodehuajianrui/archive/2009/03/18/1415173.html 

http://hi.baidu.com/630270730/blog/item/b06f628e24d173ec513d92a6.html