找回密码
 加入
搜索
查看: 15969|回复: 45

[AU3基础] 关于Floor、Ceiling、Round函数出错的问题

 火... [复制链接]
发表于 2015-3-16 17:55:48 | 显示全部楼层 |阅读模式
本帖最后由 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
EndFunc
Int($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)) ;==>8154  8154

$n=Execute(8.155*1000)
MsgBox(1,Floor($n) ,Floor(Execute(8.155*1000))) ;==>8155  8154

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)) ;==>8155  8154
在c++中int赋值后取整是得到校正的。

按此道理可以推导出Ceiling对负数表达式也会出现同样问题的,试了一下,果然:
MsgBox(1,Ceiling(-8.155*1000) ,Ceiling(-8155)) ;==>-8154  -8155

所以用Floor、Ceiling对表达式取整就用这种方式吧:
Floor(Execute(8.155*1000))

Round纠正的话试用我提供的_Round看看

评分

参与人数 1金钱 +80 贡献 +20 收起 理由
zhouhaijin + 80 + 20 非常欣赏楼主把问题分析的很彻底

查看全部评分

发表于 2015-3-16 18:01:14 | 显示全部楼层
回复 1# vuivui
10 ^ $dig,先不管其他的,这一下就不知道有没有问题
发表于 2015-3-16 18:47:16 | 显示全部楼层
蛋蛋 激情无限哈
 楼主| 发表于 2015-3-16 19:03:27 | 显示全部楼层
回复  vuivui
10 ^ $dig,先不管其他的,这一下就不知道有没有问题
netegg 发表于 2015-3-16 18:01


我想在这里是小整数应该没问题吧,复杂的浮点计算当然会有误差的。
发表于 2015-3-16 19:34:17 | 显示全部楼层
回复 4# vuivui
小整数没什么精度问题
发表于 2015-3-16 20:32:33 | 显示全部楼层
同样怀疑是二进制的问题,有人能分析一下不???
发表于 2015-3-16 21:08:13 | 显示全部楼层
用这个吧!
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  
 楼主| 发表于 2015-3-16 21:53:02 | 显示全部楼层
回复 7# gto250

有时引用其他稳定的脚本对象也是好办法
发表于 2015-3-16 22:17:07 | 显示全部楼层
感觉分析得很有道理的,大家讨论得也满热情的,论坛有点以前的感觉了
发表于 2015-3-16 23:07:05 | 显示全部楼层
LZ 的 _Round() 函数不错,本人测试无误
发表于 2015-3-17 08:39:43 | 显示全部楼层
回复 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
发表于 2015-3-17 09:34:44 | 显示全部楼层
回复 11# zldfsz
你又想犯坏了吧
发表于 2015-3-17 09:38:51 | 显示全部楼层
哈哈,正常交流
发表于 2015-3-17 12:48:28 | 显示全部楼层
本帖最后由 afan 于 2015-3-17 12:53 编辑

回复 11# zldfsz


    的确未进行大数测试,被大师的头像戳中…
看来还真得按我们对常规四舍五入的规则直接切入了,判断其后一位是否>=5,是的话就进位,就这么简单
发表于 2015-3-17 13:38:33 | 显示全部楼层
回复 14# afan

当时觉得这头像有点逗就上传了,无意冒犯,望见谅
您需要登录后才可以回帖 登录 | 加入

本版积分规则

QQ|手机版|小黑屋|AUTOIT CN ( 鲁ICP备19019924号-1 )谷歌 百度

GMT+8, 2024-5-10 08:03 , Processed in 0.109175 second(s), 34 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表