关于Floor、Ceiling、Round函数出错的问题
本帖最后由 vuivui 于 2015-3-16 21:38 编辑看了"四舍五入"无法搞定?一帖,做了个答案,发现问题有点严重,就另开一帖讨论一下。
关于Round我的答案是这样的:Func _Round($num, $dig)
$dig = 10 ^ $dig
Local $neg = False
If $num < 0 Then
$num = Abs($num)
$neg = True
EndIf
Local $numF = Int($num * $dig)
If Int($num * $dig * 10) - $numF * 10 >= 5 Then $numF += 1
$num = $numF / $dig
If $neg Then $num *= -1
Return $num
EndFuncInt($num * $dig * 10)里的Int原来我用的是Floor,但是用了Floor结果不对,原来Floor自身有问题,请看:
Floor(8.155*100*10) ;8154
Floor(8.155*1000) ;8154
Floor(9.155*100*10) ;9154
Floor(9.155*1000) ;9155
从下面的例子中可以想出问题出在8.155*1000这个表达式上:
$n=8.155*1000
MsgBox(1,Floor($n) ,Floor(8.155*1000)) ;==>81548154
$n=Execute(8.155*1000)
MsgBox(1,Floor($n) ,Floor(Execute(8.155*1000))) ;==>81558154
Int与Floor应该说对正数的取整效果是一样的,其实有很大的区别,au3来源于c++,c语言中没有Int函数,但可以通过声明整型变量赋值来实现取整,当然数据类型是int,Floor是函数,数据类型是Double。
8.155*1000二进制计算结果小于8155,不是十进制的8155整数,Floor对8.155*1000结果取整小数部分被舍去,变成了8154,中间少了一个二进制计算转换为十进制的纠正过程。Execute(8.155*1000)在au3的c++源码中应该是先用int赋值取整转换为字符串,然后Execute去作为代码去执行字符串,也就是说这样也行:
$n=8.155*1000 & ""
MsgBox(1,Floor($n) ,Floor(8.155*1000)) ;==>81558154
在c++中int赋值后取整是得到校正的。
按此道理可以推导出Ceiling对负数表达式也会出现同样问题的,试了一下,果然:
MsgBox(1,Ceiling(-8.155*1000) ,Ceiling(-8155)) ;==>-8154-8155
所以用Floor、Ceiling对表达式取整就用这种方式吧:
Floor(Execute(8.155*1000))
Round纠正的话试用我提供的_Round看看 回复 1# vuivui
10 ^ $dig,先不管其他的,这一下就不知道有没有问题 蛋蛋 激情无限哈 回复vuivui
10 ^ $dig,先不管其他的,这一下就不知道有没有问题
netegg 发表于 2015-3-16 18:01 http://www.autoitx.com/images/common/back.gif
我想在这里是小整数应该没问题吧,复杂的浮点计算当然会有误差的。 回复 4# vuivui
小整数没什么精度问题 同样怀疑是二进制的问题,有人能分析一下不??? 用这个吧!
msgbox(0,"",florx(9.155*1000))
Func florx($num)
$JS = ObjCreate("ScriptControl")
$JS.language = "jscript"
$JS.addcode("function florx(n){return Math.floor(n);}")
Return $JS.Eval("florx("&$num&")")
EndFunc 回复 7# gto250
有时引用其他稳定的脚本对象也是好办法 感觉分析得很有道理的,大家讨论得也满热情的,论坛有点以前的感觉了 LZ 的 _Round() 函数不错,本人测试无误 回复 10# afan
哪里没问题了,不是和我那篇帖子里写的那个类似吗,只不过他加入了对负数情况的判断,下面是你测试的其中一个
MsgBox(4096, "测试",_Round(123456155321234567.12345678, 2))
Func _Round($num, $dig)
$dig = 10 ^ $dig
Local $neg = False
If $num < 0 Then
$num = Abs($num)
$neg = True
EndIf
Local $numF = Int($num * $dig)
If Int($num * $dig * 10) - $numF * 10 >= 5 Then $numF += 1
$num = $numF / $dig
If $neg Then $num *= -1
Return $num
EndFunc 回复 11# zldfsz
你又想犯坏了吧 哈哈,正常交流 本帖最后由 afan 于 2015-3-17 12:53 编辑
回复 11# zldfsz
的确未进行大数测试,被大师的头像戳中…
看来还真得按我们对常规四舍五入的规则直接切入了,判断其后一位是否>=5,是的话就进位,就这么简单 回复 14# afan
当时觉得这头像有点逗就上传了,无意冒犯,望见谅