本帖最后由 老刘 于 2020-9-29 16:41 编辑
学SICP过程中了解到的算法,今天突发奇想用批处理实现一下。纯属娱乐,无效率可言。
其实有了正弦,其它的三角函数都好说。
正弦函数计算部分由@老刘编写。
浮点数四则运算由@MHL编写,在此表示感谢。
计算原理:
将输入的弧度诱导到[0,2*pi]之内。
sin通过公式sin(x)=3sin(x/3)-4[sin(x/3)]^3放缩为更小的sin角来计算。
而sin(x)与x在|x|<0.1时相差不大,故可用x替代。
目前设定的精度要求为|x|<0.0000001时使用x替代sin(x)。
注意事项:
放缩、累加过程通过递归实现,受批处理递归层数限制,可能溢出。
虽然改成迭代写法可以提高效率,但是真的懒得改了(逃)。
目前设定为保留25位小数,可自行修改。
使用方法:
不支持表达式,请分步计算。
计算结果会自动放入剪辑版,以供粘贴。
内建常量:%pi%
正弦计算:输入“sin 弧度”并回车。
四则计算:输入“[add/multiply/divide] 数1 数2”并回车。
仅核心代码无法运行,请从下面链接中下载。
https://oldliu001.lanzouj.com/iHLCmh11pcb
核心代码:纯批处理版正弦函数计算器.bat@set 保留小数位数=25
@rem 保留小数位数越大越精确,但运算速度会变慢。
@rem 由于批处理变量长度限制,不能超过100。
@set 精度=0.0000001
@rem 精度越小越精确,但运算速度会变慢。
@echo off
pushd "%~dp0"
setlocal enabledelayedexpansion
title 老刘编写——纯批处理正弦函数计算器
echo 正弦函数计算部分由@老刘编写。
echo 浮点数四则运算由@MHL编写,在此表示感谢。
echo.
echo 计算原理:
echo 将输入的弧度诱导到[0,2*pi]之内。
echo sin通过公式sin^(x^)=3sin^(x/3^)-4[sin^(x/3^)]^^3放缩为更小的sin角来计算。
echo 而sin^(x^)与x在^|x^|^<0.1时相差不大,故可用x替代。
echo 目前设定的精度要求为^|x^|^<!精度!时使用x替代sin^(x^)。
echo.
echo 注意事项:
echo 放缩、累加过程通过递归实现,受批处理递归层数限制,可能溢出。
echo 虽然改成迭代写法可以提高效率,但是真的懒得改了(逃)。
echo 目前设定为保留!保留小数位数!位小数,可自行修改。
echo.
echo 使用方法:
echo 不支持表达式,请分步计算。
echo 计算结果会自动放入剪辑版,以供粘贴。
echo 内建常量:%%pi%%
echo 正弦计算:输入“sin 弧度”并回车。
echo 四则计算:输入“[add/multiply/divide] 数1 数2”并回车。
echo.
echo.
echo 关闭句柄1(标准输出)。>nul 3>nul
rem set /a 保留小数位数3倍=保留小数位数*3
set /a 保留小数位数+=1
set pi=3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679
call :_round pi !保留小数位数!
call "multiply" !pi! 2
set _2pi=!errorlevel!
call :_round _2pi !保留小数位数!
:loop
set /p "command=>>> ">con
call :!command!
echo.>con
goto loop
:add
call "plus&minus" %1 %2
echo 计算结果:!errorlevel! >con
set /p "=!errorlevel!"<nul | clip
goto :eof
:divide
call "divide" %1 %2 %保留小数位数%
echo 计算结果:!errorlevel! >con
set /p "=!errorlevel!"<nul | clip
goto :eof
:multiply
call "multiply" %1 %2
echo 计算结果:!errorlevel! >con
set /p "=!errorlevel!"<nul | clip
goto :eof
:sin
call :_sin %1
echo 计算结果:!errorlevel! >con
set /p "=!errorlevel!"<nul | clip
goto :eof
:_sin
setlocal
set number=%1
:__sin_loop
call :_isBigger !number! !_2pi!
if !errorlevel! == true (
call "plus&minus" !number! -!_2pi!
set number=!errorlevel!
goto __sin_loop
)
call :_isBigger !number! 0
if !errorlevel! == false (
rem 负弧度,转为正弧度。
call :_sin !number:~1!
set dest=!errorlevel!
call :_isBigger !dest! 0
if !errorlevel! == true (
set dest=-!dest!
)
) else (
call :_isBigger !number! !精度!
if !errorlevel! == false (
rem 放缩完毕,直接返回。
set dest=!number!
) else (
rem 通过sin^(x^)=3sin^(x/3^)-4[sin^(x/3^)]^^3进行放缩。
call "divide" !number! 3 %保留小数位数%
call :_sin !errorlevel!
set sinNumberDivide3=!errorlevel!
call "multiply" !sinNumberDivide3! 3
call :_round errorlevel %保留小数位数%
set destPart1=!errorlevel!
>con set sinNumberDivide3
call "multiply" !sinNumberDivide3! !sinNumberDivide3!
call :_round errorlevel %保留小数位数%
call "multiply" !errorlevel! !sinNumberDivide3!
call :_round errorlevel %保留小数位数%
call "multiply" !errorlevel! 4
call :_round errorlevel %保留小数位数%
set destPart2=!errorlevel!
>con set destPart2
rem destPart2必定是正数。
call "plus&minus" !destPart1! -!destPart2!
set dest=!errorlevel!
call :_round dest %保留小数位数%
)
)
for %%a in (!dest!) do endlocal&set errorlevel=%%a
goto :eof
:_isBigger number1 number2
rem 若数1比数2大(或等),返回true,否则返回false。
setlocal
set number2=%2
if "!number2:~,1!" == "-" (
set "number2=+!number2:~1!"
) else (
set "number2=-!number2!"
)
rem echo call "plus&minus" %1 !number2! >con
call "plus&minus" %1 !number2!
if "!errorlevel:~,1!" == "-" (
set dest=false
) else (
set dest=true
)
for %%a in (!dest!) do endlocal&set errorlevel=%%a
rem echo 计算结果:!errorlevel! >con
rem set /p "=!errorlevel!"<nul | clip
goto :eof
:_round 四舍五入函数(提高精度用) 变量 保留小数位数-1
setlocal
rem 备份%1,使其支持处理errorlevel。
set number=!%1!
rem 若无小数部分,直接返回。
if "!number!" equ "!number:.=!" goto :eof
rem 若位数比保留位数少,直接返回。
set demicalPart=!number:*.=!
set /a __round_tmp=%2-1
for %%a in (!__round_tmp!) do (
if "!demicalPart:~%%a,1!" equ "" goto :eof
)
rem 从最后一位向前四舍五入到指定的位数。
set carry=0
:__round_loop
set /a lastNum=!demicalPart:~-1!+carry
set carry=0
if !lastNum! geq 5 set carry=1
:__round_jumpin
if "!demicalPart:~%2,1!" equ "" (
set demicalPart=!demicalPart:~,-1!
set /a lastNum=!demicalPart:~-1!+carry
set carry=0
goto __round_loop2
)
set demicalPart=!demicalPart:~,-1!
goto __round_loop
rem 若出现9+1,则继续进位。
:__round_loop2
if !lastNum! equ 10 (
set demicalPart=!demicalPart:~,-1!
set /a lastNum=!demicalPart:~-1!+1
if "!demicalPart!" neq "" (
goto __round_loop2
) else (
set demicalPart=0
set carry=1
)
) else (
set demicalPart=!demicalPart:~,-1!!lastNum!
)
call "plus&minus" !number! !carry!
for /f "tokens=1,3 delims=. " %%a in ("!errorlevel! !demicalPart!") do endlocal&set %1=%%a.%%b
rem echo 计算结果:!%1! >con
rem set /p "=!%1!"<nul | clip
goto :eof
|