昨天在VB中碰到两个问题,其实以前就碰到并解决过:
(1)
dim ss as double
ss = 194268.02 – 194268
肉眼可以判断结果为0.02,而VB中计算的结果:ss = 0.199999999895226E-02
ss = 1.2 - 1 VB计算的结果为:0.2
(2)
dim ss as double
ss = 400*1000
在VB6中,报越界!!
ss = 400*100000没有异常
分析原因:1:内部二进制与10进制的转换导致Double运算精度问题
2:Int16* Int16VB6以为应该返回Int16,但结果>32767导致越界错误,因此ss=2+32766也会导致溢出,经证实确实如此
这算不算BUG呢?我认为是.
.NET中是否还存在类似问题呢?
经测试在.NET中问题2表面看来已经不存在,但是:
double ss;
int firstInt = 2147483646;
int secondInt = 2;
ss = firstInt + secondInt;
Console.WriteLine(ss);
结果:ss = -2147483648
CLR用int32作为缓冲区,但如果我们的运算结果超出该缓冲区的大小,一样会越界!!
.NET中问题(1)依然如故
这两个问题通过类型转换可以轻松解决,我疑惑的是:从OOP的观点看待该问题,是程序员应该掌握规避方法,还是改变其内部处理机制?你如何看呢?
打印 | 张贴于 2004-12-07 09:49:00 | Tag:暂无标签
留言反馈
dataid="x90x10"
dataid_l = Asc(Mid(dataid, 1, 1))
dataid_r = Asc(Mid(dataid, 2, 1))
MsgBox "正在接收..." & Right(Hex(dataid_l), 2) & Right(Hex(dataid_r), 2), vbOKOnly, "接收"
为什么显示出来的是正在接收...7839
恳求各位大侠指点。
ss = 194268.02 – 194268
肉眼可以判断结果为0.02,而VB中计算的结果:ss = 0.199999999895226E-02
(这里应该是打错了) 应该是:1.99999999895226E-02
手档车自然操作起来容易,但是开自动档的有愿意研究手档怎么开的吗?
ss = 194268.02 – 194268
小弟也遇到过这个问题,不过按照小弟的经验
ss.ToString()
的值又会是0.02
不知何解
没有仔细看.不过 CDEC 是纠正浮点错误.你的答案是 0.02 呵呵.抱歉
CDEC(DOUBLE1)-CDEC(DOUBLE2) 就可以了.
这样会自动纠正浮点错误的.
这些东西如果让搞汇编的来看,肯定觉得是天经地义的事情吧
Dim x As Double = "3" * 4
MsgBox(x)
关于这个什么数啊数的,不如用python好了,新版2.4刚刚实现长整数和普通整数的自动转换,保证不会像vb挖个陷阱让你跳。
而且这些大部分都是手册可以找到的规则,实际上印象中msdn是包括了对vb怎么对数值进行自动转换的顺序和规律的文档的。
我很遗憾你得到了这样的结论。
第一个:我想大部分的编程入门手册都会告诉我们浮点数的精度问题,以及浮点数不能精确表示所有实数的问题,这个是很基础的东西。为什么不应该直接比较两个浮点运算结果等等,这些都是很基础的知识。你用其他语言的浮点运算,也会得到类似的结果
int main()
{
double ss;
ss = 194268.02 - 194268;
return 0;
}
这样的程序你在vc里面跟踪一下,结果是类似的。
第二个:我已经说了,弱类型语言是会自动转换,但是有规律。你的做法违反了规律当然会发生错误。任何语言都有自己的规定(包括一些隐含的),你违反了规定,当然会出现错误。
这里你的实验也可以稍微揭示一下这个转换的规律。
另外,如果需要处理的异常,不去处理,出现问题那当然是程序员的责任。
-----------------------------
我希望你把你的好奇心和精力放在更有意思的地方。比如第一个问题,如果你好好去看看关于浮点数存储的方式,或者自己用比较底层的方式实现一个浮点数加减法,你就会明白,为什么在浮点运算里面194268.02 – 194268居然不完全等于1.2 - 1(实际上,一般是用两个运算结果的差值是都小于一个足够小的数,来判断相等与否的)。这个实验做了以后,你会对浮点数有更深刻的认识。
第二个问题,你可以通过一系列的类似试验得知,vb的自动转换规则。虽然你刚才在抱怨这个转换规则达不到你的要求,但是实际上,转换规则是在效率,已经最大程度减少二义性的前提下的一个折中的结果。正像很多技术都是折中的结果一样。
其实是C#/VB.NET中问题产生的条件更难了,并不是就不存在了。正如你后面的例子一样。字面常量的类型是应当指定的,这在VB6,C#或VB.NET中都有完美的语法支持。
至于第一个问题……二进制浮点数无法准确表示0.02,0.02在二进制浮点数中是个循环小数。
事实上,这些值并是不可预期的,事实上我也是使用转换类型的方法解决该问题,但涉及到一个责任划分和分工的问题,我觉得,这种问题不应该是VB程序员职责所在.
"ss = 400*1000 当然会溢出"为何是"当然"?起码.NET中不会溢出,这是内部处理机制的问题
想起我以前的一个项目,有程序员包装了一个Grid控件,只有鼠标在上面转几个圈,程序就异常退出,原因后来查明,是程序捕获鼠标事件,导致溢出,我们是否应该让用户记住这一点,使用时避免移动鼠标呢?还是修改我们的程序?
OOP很重要的一点就是责任划分,我是这样看待这个问题的
ss = 0.199999999895226E-02
和
ss=0.2 可以认为都是正确的。
第二个问题是你自己的错误
ss = 400*1000 当然会溢出
正确写法是
ss = 400.0*1000.0
或者
ss = 400# * 1000#
basic是弱类型语言会自动进行转换没错,但是转换的方式有规律可循的,你也总结了是因为超过了Int16。所以问题在于你用错误的方式去执行这个运算。
ss = 400*100000 不出现问题也相当正常100000这个常量显然超越了Int16的范围。