标题: [数值计算] [原创]通用批处理开平方程序,理论上支持任意数字 [打印本页]
作者: techon 时间: 2011-5-26 01:23 标题: [原创]通用批处理开平方程序,理论上支持任意数字
本帖最后由 techon 于 2011-5-27 19:15 编辑
废话一大堆,删了
方法原理见算数开平方法则
通用性处理,可支持任意数字输入,结果保留8-9位有效数字
其他算法:
二楼 版主的 牛顿迭代法快速求平方根或近似值,只能计算1-214748,保留两位小数
七楼 caruko的 使用二进制位移法开平方,支持1-2147483646,最多保留4位小数
代码如下:
保留了部分测试代码和注释,欢迎批评指正
用法 call :_Sqrt 被开方数字- @echo off
- SETLOCAL ENABLEDELAYEDEXPANSION
- rem 这是测试部分
- set x=1.4233599273013201
- set max=14233599273013201
- rem set Max = 2147483647 小于2的31次方-1(2^31=2147483648)32位有符号数可表示的最大值 set/a 可处理的最大数(32位系统是这个,64位系统未测试)
- rem 11930464 * 20 * 9 + 9^2 = 2147483601 11930464^2 = 142335971255296
- rem 119304649 119304649^2 = 14233599273013201 (可正常开完的最大被开方数)
-
- rem %1=被开方数 这是结果写在这方便比对
- call :_Sqrt 2250000000000 1500000
- echo --------jg===%jg%---------&pause
- call :_Sqrt 1000000 1000
- echo --------jg===%jg%---------&pause
- call :_Sqrt 0 0
- echo --------jg===%jg%---------&pause
- call :_Sqrt 00000.00000001 0.0001
- echo --------jg===%jg%---------&pause
- call :_Sqrt 26 5.0990195135927848300282241090228
- echo --------jg===%jg%---------&pause
- call :_Sqrt %x% 1.19304649
- echo --------jg===%jg%---------&pause
- call :_Sqrt %max% 119304649
- echo --------jg===%jg%---------&pause
- call :_Sqrt 11112233445643274231572 105414578904.64332842651254712323
- echo --------jg===%jg%---------&pause
- call :_Sqrt 2.250000000000000 1.5
- echo --------jg===%jg%---------&pause
- call :_Sqrt 0.000001 0.001
- call :_Sqrt 01111.5643274231572 33.340130884913412409133565742286
- call :_Sqrt 1048576 1024
- call :_Sqrt 0.0132010 0.114895604
- call :_Sqrt 0.00000064 0.0008
- echo --------jg===%jg%---------&pause
- call :_Sqrt 55 7.4161984870956629487113974408007
- call :_Sqrt 4.00000000 2
- echo --------jg===%jg%---------&pause
- rem 做成子程序了,方便调用
- pause&pause
- goto :EOF
-
- rem 开方程序开始
- :_Sqrt
- SETLOCAL ENABLEDELAYEDEXPANSION
- rem 检查输入是否有效
- set a=0&set sr=0&set tmpin=%1
- if "!tmpin!"=="" set sr=-1&goto :chkend
- :chkloop
- if "!tmpin:~%a%,1!"=="." (set/a sr+=1&if !sr! gtr 1 set sr=-1&goto :chkend) ^
- else (if !tmpin:~%a%^,1! lss 0 set sr=-1&goto :chkend)&if !tmpin:~%a%^,1! gtr 9 set sr=-1&goto :chkend
- set/a a+=1
- if not "!tmpin:~%a%,1!"=="" goto :chkloop
- :chkend
-
- rem 初始化变量
- set jg=0&set xs=0&set xsl=0&set fxs=0&set of=0&set pl=0&set d=0&set x1=&set x2=&set tlc=
-
- if %sr% equ -1 ENDLOCAL&set jg=被开方数字非法&echo Call :_Sqrt 输入的参数无效&goto :EOF
- if %sr% equ 0 ( rem 无小数点
- call :_dez l %tmpin% xa
- if "!xa!"=="" set xa=0
- ) else ( rem 有小数点输入
- for /f "tokens=1,2 delims=." %%C in ("0%tmpin%") do (
- call :_dez l %%C
- set x1=!dez!
- call :_dez r %%D
- set x2=!dez!
- rem 处理输入数 可将 0.xxx 简略为 .xxx 输入,取输入的整数部分为 x1,小数部分为x2
- if "!x2!"=="" ( rem 没有小数
- if "!x1!"=="" (set xa=0) else set xa=!x1!
- ) else ( rem 有小数
- call :_lenc !x2!
- set/a tlc=lenc%%2
- if !tlc! equ 1 set x2=!x2!0
- rem 小数位数如果为奇数,将其后补0,凑为偶数位
- set/a xsl=^(lenc+1^)/2
- rem 输入小数的位数除以2,开完平方后需要补回的小数位数
- if "!x1!"=="" call :_dez l !x2! x2
- set xa=!x1!!x2!
- )
- )
- )
-
- if "%x2%"=="" ( rem 如果没有小数部分,去掉整数低位2n个0,设置小数位数为负
- :lx1
- if "!xa:~-2!"=="00" set xa=!xa:~0,-2!&set/a xsl-=1&goto :lx1
- )
-
- set j19=0
- for /l %%C in (0,1,9) do ( rem 如果是1-9的平方数直接给出结果
- set/a jc=%%C*%%C
- if "!xa!"=="!jc!" (
- if %xsl% equ 0 echo 结果=%%C&ENDLOCAL&set jg=%%C&goto :EOF
- set _jg=%%C&set jg=%%C
- :xsub
- if !xsl! lss 0 set jg=!jg!0&set/a xsl+=1&goto :xsub
- :xsad
- if !xsl! gtr 0 set jg=0.!_jg!&set _jg=0!_jg!&set/a xsl-=1&goto :xsad
- set j19=1
- )
- )
- if "%j19%"=="1" echo 结果=%jg%&ENDLOCAL&set jg=%jg%&goto :EOF
-
- call :_lenc %xa%
- set/a la="lenc%%2"
- rem 将xa补位为偶数位
- if %la% equ 1 set xa=0%xa%
-
- rem 开方循环开始
- :_sqr
- set p=!xa:~%pl%,2!
- if %d% equ 0 (
- if "%p%"=="" (
- echo 开方正好开尽^^_^^&goto :_endsqr
- ) else (
- if %jg% gtr 11930464 set of=1&echo 产生溢出!!!&goto :_endsqr
- if "!p:~0,1!"=="0" (set ps=%p:~1,1%) else (set ps=%p%)
- )
- ) else (
- if %jg% gtr 11930464 set of=1&echo 产生溢出!!!&goto :_endsqr
- if "%p%"=="" set p=00&set/a xs+=1
- set ps=%d%!p!
- )
- for /l %%C in (9,-1,0) do (
- set/a ts=20*%%C*jg+%%C*%%C
- if !ts! leq %ps% (
- if not "%jg%"=="0" (set jg=%jg%%%C) else set jg=%%C
- set/a d=ps-ts&goto :_sh
- )
- )
- :_sh
- set/a pl+=2&goto :_sqr
- rem 开方循环结束
- :_endsqr
-
- if not "!xa:~%pl%!"=="" (
- call :_lenc !xa:~%pl%!
- if "!tlc!"=="1" (set/a fxs=lenc-1) else (set fxs=!lenc!)
- echo 忽略位数:fxs===!fxs! rem 溢出后将被开方数忽略的位数
- )
- set/a xsl=xsl+xs-(fxs+1)/2
- rem 修正小数位数
- echo 小数位数:%xsl% rem 开方结果小数的位数
- if %fxs% gtr 0 if %xsl% lss 0 echo 非有效位数 %xsl:~1%
-
- if %xsl% gtr 0 (
- call :_lenc %jg%
- if !lenc! gtr %xsl% (
- set jg=!jg:~0,-%xsl%!.!jg:~-%xsl%!
- ) else (
- set/a jgl=xsl-lenc
- :ld0
- if !jgl! gtr 0 set jg=0%jg%&set/a jgl-=1&goto :ld0
- set jg=0.%jg%
- )
- )
- :ad0
- if %xsl% lss 0 set jg=%jg%0&set/a xsl+=1&goto :ad0
- if %of% equ 1 (set fh=≈) else (set fh==)
- echo 结果%fh%%jg%&ENDLOCAL&set jg=%jg%&goto :EOF
- rem End_Sqrt
-
- rem 计算字串长度子程序
- :_lenc
- SETLOCAL ENABLEDELAYEDEXPANSION
- set n=0&set _#=^"&set "stmp=%1"
- :asa
- if not "!stmp:~%n%,1!"=="" set/a n+=1&goto asa
- if "!stmp:~0,1!"=="!_#!" set/a n-=2
- ENDLOCAL&set "lenc=%n%"
- goto :EOF
- rem End_lenc
-
- rem 删除数字串两端0的子程序,三个参数:%2 为待处理的数字串;%1=l时删除数字串左端的0,%1=a时删除两端的0
- rem 其他值时删除右端的0;%3 为变量名,保存处理完的字串。用法:Call :_dez %1=l|r|a [%2=String [%3=var]]
- :_dez
- SETLOCAL ENABLEDELAYEDEXPANSION
- set str=%2&if "%3"=="" (set var=dez) else set var=%3
- if "%1"=="l" (set ts1=0,1&set tss=1) else set ts1=-1&set tss=0,-1
- :loopz
- if "!str:~%ts1%!"=="0" set str=!str:~%tss%!&goto :loopz
- if "%1"=="a" if "!ts1!"=="-1" set ts1=0,1&set tss=1&goto :loopz
- ENDLOCAL&set %var%=%str%&goto :EOF
- rem End_dez
-
- rem ---------------------------------------------------All End
复制代码
-----------------------------------------------------------------------------------
另附除法计算部分,仅支持整数输入
用法 call :_div 被除数 除数 [保留小数位,如精度不够则不保留]
返回结果 保存在变量quo中
例:- call :_div 5 3 3
- echo 5÷3 结果 %quo%
- rem 5除以3 保留3位小数
- call :_div 16 9
- echo 16÷9 结果 %quo%
- rem 16除以9 能算几位算几位
- pause:goto :eof
-
- rem 除法计算
- :_div
- if "%1"=="" echo 请输入被除数(参数 %%^1)&goto :EOF
- if "%2"=="" echo 请输入除数(参数 %%^2)&goto :EOF
- SETLOCAL ENABLEDELAYEDEXPANSION
- set Maxd=2147483647&set scp=0&set ded=%1&set dvr=%2&set quo=&set psc=%3
- if "%psc%"=="" set psc=%Maxd%
- if %dvr% equ 0 ENDLOCAL&echo 错误,除数为零!&goto :EOF
- if %ded% equ 0 ENDLOCAL&set quo=0&goto :EOF
- if "%ded%"=="%1" (if not "%dvr%"=="%2" ENDLOCAL&echo 参数 %%^2 输入错误或数值超限&goto :EOF)^
- else (echo 参数 %%^1 输入错误或数值超限
- if not "%dvr%"=="%2" ENDLOCAL&echo 参数 %%^2 输入错误或数值超限&goto :EOF)
- :divbg
- set/a quo=ded/dvr, rdd=quo*dvr
- if %rdd% neq %ded% if %ded% leq %Maxd:~0,-1% (
- if %scp% lss %psc% set ded=%ded%0&set/a scp+=1&goto :divbg
- )
- if %quo% equ 0 ENDLOCAL&set quo=0&goto :EOF
- if %scp% neq 0 if "!quo:~-%scp%!"=="!quo!" (
- set quo=00000000%quo%&set quo=0.!quo:~-%scp%!)^
- else set quo=!quo:~0,-%scp%!.!quo:~-%scp%!
- ENDLOCAL&set quo=%quo%&goto :EOF
复制代码
作者: batman 时间: 2011-5-26 01:32
楼主用这种算法好些不?
http://www.bathome.net/viewthrea ... hlight=%B5%FC%B4%FA
作者: techon 时间: 2011-5-26 01:44
本帖最后由 techon 于 2011-5-26 02:13 编辑
近似值算法,简单快捷,对于精度要求不高和计算不太大的数非常好用
算个这个数就不行了:1099511627776
2的40次方 1048576的平方
作者: caruko 时间: 2011-5-26 02:20
开方,即将数字的2进制位截取一半..
使用移位算法,复杂度会大幅下降。
比如 4*4 = 16 , 2^2=4, 2^4=16; 把4位二进制数变成2位即可。
又如,2^4=16, 16*16=256, 2^(4*2)=256 。
反过来开方 256 就是把2^(8/2)。
作者: techon 时间: 2011-5-26 02:24
请问楼上的兄弟?
7的二进制怎么处理? 0111
如何处理小数点呢?
作者: caruko 时间: 2011-5-26 02:51
本帖最后由 caruko 于 2011-5-26 03:59 编辑
任何整数都是 1 2 4 8 ... 这些数字之和
7 = 2^2 + 2^1 +2^0 ,即 set /a "(1<<2)+(1<<1)+(1<<0)"
(2^2 + 2^1 +2^0)*(2^2 + 2^1 +2^0) = (2^4 + 2^3 + 2^2) + (2^3 + 2^2 + 2^1) + (2^2 + 2^1 + 2^0)
就相当于嵌套的 for %%i in (2 1 0) do for %%j in (2 1 0) do set /a v+=1"<<"(%%i+%%j)
好吧,完全平方的多项式分解很麻烦。
算法如下
实际上,得出 1<<5 小于 49 小于 1<<6,可以得出 49的根为 x=(5/2) ,即1<<2位, 3位2进制数。
for /l %%i in (x,-1,0) do (
set /a v+=1<<%%i
if v*v gtr 49 set /a v-=1<<%%i
if v*v equ 49 ( break & echo v )
)
如果仍然未出结果,就涉及小数。
那么把 v 左移 2位,把 49 左移4位,继续计算。在未溢出的情况下,算完之后右移2位,就能得到 2位小数的根了。
作者: caruko 时间: 2011-5-26 12:49
本帖最后由 caruko 于 2011-5-26 14:37 编辑
按照上面的算法,我也放一个开方代码。
32位限制,精确度在5位左右。
修改了一个数值接近MAX时,导致变量k为空的,无法输出结果的小错误。- @echo off&SETLOCAL ENABLEDELAYEDEXPANSION
- ::最大被开方数,2147483647 < 2^31
- ::最大开方数, < 2^16
- ::每次都将输入数值增加10^2n次,尽量增加到接近MAX的数,
- ::检测输入
- :loop
- 2>nul set /a 1/%1 &&(set "input=%1")||(set /p input=请输入被开方数字:)
- 2>nul set /a 1/input ||goto :loop
- call :kf !input!
- goto :eof
-
- :kf
- setlocal
- ::在溢出范围内提高位数,增加精确度。
- set /p=被开方数:!input!,结果:<nul
- set /a input=%1,cs=1
- for /l %%i in (1,1,8) do (
- set /a cs*=10
- if "!cs:00=!"=="1" (
- set /a x=input*cs,y=x/cs
- if !input! equ !y! set /a tp=x,k=%%i/2
- ))
- if "!tp!"=="" (
- set /a k=0
- ) else (
- set /a input=tp
- )
- :: 检测被开方数2进制的位数。
- set "ofset="
- for /l %%i in (0,1,31) do (
- set /a v=1"<<"%%i
- if %%i equ 31 set /a v=v+1&set v=!v:-=!
- if not defined ofset if !v! gtr !input! set /a ofset=%%i-1
- )
- if not defined ofset (
- echo,输入数字超标!
- exit /b -1
- )
- set /a kofset=ofset/2,v=0
- ::求被开方数,整数部分。
- for /l %%i in (!kofset!,-1,0) do (
- set /a v+=1"<<"%%i,flag=v*v
- if !flag! gtr !input! set /a v-=1"<<"%%i
- )
- for /f %%a in ("!k!") do set ecode=!v:~0,-%%a!.!v:~-%%a!
- if !k! equ 0 set ecode=!v!.0
- echo,!ecode!
- endlocal
复制代码
作者: applba 时间: 2011-5-26 18:25
本帖最后由 applba 于 2011-5-26 18:26 编辑
批处理做浮点运算,真是是强人所难啊……
不过我还是对两位数学强人表示由衷的钦佩……
我只主张应用为王……
欢迎光临 批处理之家 (http://www.bathome.net/) |
Powered by Discuz! 7.2 |