老刘 发表于 2020-9-29 16:40

[批处理][算法实战009]正弦函数计算器

本帖最后由 老刘 于 2020-9-29 16:41 编辑

学SICP过程中了解到的算法,今天突发奇想用批处理实现一下。纯属娱乐,无效率可言。
其实有了正弦,其它的三角函数都好说。
正弦函数计算部分由@老刘编写。
浮点数四则运算由@MHL编写,在此表示感谢。

计算原理:
    将输入的弧度诱导到之内。
    sin通过公式sin(x)=3sin(x/3)-4^3放缩为更小的sin角来计算。
    而sin(x)与x在|x|<0.1时相差不大,故可用x替代。
    目前设定的精度要求为|x|<0.0000001时使用x替代sin(x)。

注意事项:
    放缩、累加过程通过递归实现,受批处理递归层数限制,可能溢出。
    虽然改成迭代写法可以提高效率,但是真的懒得改了(逃)。
    目前设定为保留25位小数,可自行修改。

使用方法:
    不支持表达式,请分步计算。
    计算结果会自动放入剪辑版,以供粘贴。
    内建常量:%pi%
    正弦计算:输入“sin 弧度”并回车。
    四则计算:输入“ 数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   将输入的弧度诱导到之内。
echo   sin通过公式sin^(x^)=3sin^(x/3^)-4^^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   四则计算:输入“ 数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^^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

yiwozhutou 发表于 2020-9-29 18:55

不知道干嘛用 要是军事用途比如射击角度什么或者自动瞄准 可以用吗?
页: [1]
查看完整版本: [批处理][算法实战009]正弦函数计算器