SUMTEC -- There's a thing in my bloglet.

But it's not only one. It's many. It's the same as other things but it exactly likes nothing else...

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::
好久没有上来写点什么东西了,也有一段时间没有到博客堂拨客园上面来采风了,今天一上来就看到这个文章:
数据类型的BUG还是???

里面罗列了一些问题,也许我能略解一二。比如说问题二:

dim ss as double

ss = 400*1000

在VB6中,报越界!!

ss = 400*100000没有异常

其实是这样的,VB6里面对于常数,如果没有带数据类型标志符(例如#)或者小数点,就认为是整数。而对于实际上是什么整数,则根据最小化原则,认为400和1000同为16位带符号整形。而数值计算结果的数据类型和操作数中表示范围最大的相一致(其实大部分语言都是这么定义的。ps:对于VB6及以下版本,似乎没有应用常量传播,要到计算里面才会出错),很明显400*1000的计算结果超出16位带符号整形的表示范围,报越界。而400*100000里面后者被认为是32位带符号数值,因此计算结果也是32位带符号数值,所以不会越界。

请注意,大部分语言的计算过程是有一个中间计算结果的,这个结果跟最终承载变量没有关系,而跟该语言的运算法则相关。在本例当中无论ss被定义为double、long还是别的数值类型,都必然会引发越界,这是由VB6里面的语言定义所引起的。而中间计算结果要经过一个转换过程才能够得到最终变量的数据类型,一般的基本数据类型之间都有“隐式”转换,有的是强制转换,这个一般由语言本身所定义。例如在VB6里面,几乎所有数值类型之间都能够自由的进行隐式转换,但是在C#里面,浮点数转换为整形数字的时候就必须要强制转换。

当然,从某种角度来讲,所有数值类型之间能够自由的隐式是VB6语言定义本身的缺陷,因为这样可能会引发很多“看不见”的问题。但是这实际上是VB6语言本身的定义,而不是设计人员无意识的或者不期望的结果,所以我宁愿称之为Fault也不愿意说是Bug。

可是大坏蛋却说.NET里面:
 double ss;
 int firstInt = 2147483646;
 int secondInt = 2;
 ss = firstInt + secondInt;
 Console.WriteLine(ss);

结果:ss = -2147483648

似乎对这个现象有点意见。首先,还是那个原因,计算是有中间结果的,中间结果的类型在这里仍然是int。其次,要追溯C语言本身的处理方式,在C语言里面不会对整形的上下界超界产生任何疑问,甚至不会报错。因为这个被认为是C语言的“特性”之一,C#“号称”继承了C/C++,那自然也会尽可能继承这些传统习惯,因此他就作为语法规范里面的一部分了,无可厚非。而事实上这也不是.NET Framework的功劳,而仅仅是C#的定义而已。因为在VB.NET里面,这会产生异常的。因为在C#的编译器对整数加减法使用的是不带检验的IL指令,而VB.NET则使用的是待检验的IL指令。比如C#使用的是add指令,而VB.NET则使用的是add.ovf指令。当然,这是在最普遍的代码编写方式,以及默认的语言参数下面而言的。

如果有什么疑问,请尝试下述代码:

 int ss;
 int firstInt = 2147483646;
 int secondInt = 2;
 ss = firstInt + secondInt;
 Console.WriteLine(ss);

呵呵,现在再请没有疑问的尝试下述代码:
int ss = int.MaxValue + 2;

回过头来我们再看看第一个问题:

dim ss as double

ss = 194268.02 – 194268

肉眼可以判断结果为0.02,而VB中计算的结果:ss = 0.199999999895226E-02

ss = 1.2 - 1 VB计算的结果为:0.2


要知道这个问题的答案,我们首先要看看这里的浮点数到底是什么浮点数。在.NET Framework里面(以及VB/VC等)遵循的是IEEE标准,那么为什么0.02不是0.02了呢?其实这个在IEEE里面可以找到一个快速的解答。那么为什么后面一个计算会是正确的呢?那其实是因为“精度”足够,使得你认为它就是0.2。事实上IEEE浮点数永远不可能精确等于2的n次幂相加所构成的数值(比如1.375 = 20 + 2-2 + 2-3,我“简称”这种数字为“可被2整除的数字”。),除非IEEE更改了他的标准。(关于IEEE浮点数的定义可以参考这里。)顺带给出double的1.2和0.2的十六进制编码:
1.2 = IEEE_double(3FF3 3333 3333 3333)
0.2 = IEEE_double(3FC9 9999 9999 999A)
而1.2-1的运算结果却是 IEEE_double(3FC9 9999 9999 9998),看到了吗?其实1.2 - 1并不等于0.2的。而事实上IEEE浮点运算即使是在“可被2整除”的数字之间进行,通常都会有误差的,这主要源于精度丢失。前面的1.2 - 1的误差并不属于这个范畴,这主要是由于操作数本身无法被精确表示而造成的(虽然也有精度丢失的原因)。可以说精度不丢失的情况是相当特殊的,比如说完全相等的两个数相加减,乘、除以2的整倍或者正负1以及0,和0或者“非数字”之间的计算,等等。

所以说这些问题千万不要往MS的头上扣,也不是MS所能够改变得了的。

posted on 2004-12-08 17:50  Sumtec  阅读(2365)  评论(2)    收藏  举报