Fastest sin(x) in dos batch

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Fastest sen(x) in dos batch

#16 Post by einstein1969 » 11 Sep 2013 09:52

Thanks Aacini, you reminded me of the old days.

In my career I have used the old turbo pascal 3.0 and I developed these things in assembler 8088 but I had forgotten that I did ...

However, I was interested to see how many digits you can get the 32bit integer math. In fact in my function INT_sin I arrived at this time to handle 5 digit without having to use too many tricks. To make a complete automatism should handle exceptions for those calculations that exceed the 32-bit and I think there is a thread where it was discussed.

But there are also differences between xp and win 7.

In my function I implemented the shift with the division :) . and I use it with care. I'd like to calculate the error committed in the operations in order to choose the best order in which to run. Thank you again!

Francesco aka Einstein1969

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Fastest sin(x) in dos batch

#17 Post by einstein1969 » 16 Sep 2013 10:38

For test the Int_sinD i had to compare result with a solid version of sin(x). Is born a new function that is a hibryd for table lookup and other trick.

This calculates sine and cosine together!

This is a function that use about 28bit at the moment to achieve 7 decimal of precision in output (0.0000001) and input in 100*microDegree (0.0001)

There is optimization that i can do for faster execution?

How can break the for loop in safe mode for use in a macro?

Code: Select all

@echo off & setlocal EnableDelayedExpansion

if "%~1"=="" (set angle=300000) else set "angle=%~1"

call :INT_SinD_CosD %angle% sin cos

echo(
echo Angle:(%angle%/10000^)° Integer raw result sin=!sin! cos=!cos! & Rem Use Ansi, chcp 1252

rem pad
call :pad_sin_cos sin & call :pad_sin_cos cos

echo(
echo Pad: sin=!sin! cos=!cos!

goto :eof

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:INT_SinD_CosD  Angle  SinResultVAR  CosResultVAR
::
::      -- Return the sine and cosine for the specified angle in 100*micro-degrees.
::         ie. 10000 is one degree.
::
::         Call :INT_sinD_CosD Angle sin cos
::            
::      -- Angle    [in]   in 100 micro-Degree is an integer in [-449999,449999]
::
::      -- SinResultVAR    [out]   var reference to return value of sine [-10000000,10000000]
::
::      -- CosResultVAR    [out]   var reference to return value of cosine [-10000000,10000000]
::
::
::  Version history
::
:: 0.1   16/9/2013   
::
::         First version Beta! Internal 27bit used
::
::         From Francesco Poscetti aka Einstein1969
::
::         NOTE: This Function allow you to calculate almost all other common
::         trigonometric function. Uses the CORDIC modified algorithm.
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
SetLocal EnableDelayedExpansion

   set /a "a=%~1"

   if %a% lss -449999 echo Not implemented. Exit. & goto :eof
   if %a% gtr  449999 echo Not implemented. Exit. & goto :eof

   set /a size=-1
   For %%a in (450000000 265650512 140362435 71250163 35763344 17899106
   8951737 4476142 2238105 1119057 559529 279765 139882 69941 34971 17485
   8743 4371 2186 1093 546 273 137 68 34 17 9 4 2 1) do (set /a "size+=1" & set "at!size!=%%a")

   if %a% geq 0 (     set /a x=607252935, angle=!a!*10*10*10, angle-=!at0!, y=x
               ) else set /a x=607252935, angle=!a!*10*10*10, angle+=!at0!, y=-x

   For /L %%# in (1,1,!size!) do (
     if !angle! lss 0        (set /a "angle+=!at%%#!, dx=x/(1<<%%#), x+=y/(1<<%%#), y-=dx"
     ) else if !angle! gtr 0 (set /a "angle-=!at%%#!, dx=x/(1<<%%#), x-=y/(1<<%%#), y+=dx")
   )

endlocal & set /a %2=(%y%+55)/100, %3=(%x%+55)/100
goto :eof
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:pad_sin_cos num
  setlocal EnableDelayedExpansion
    if !%1! lss 0 (set "sign=-" & set /a ris=-%1) else (set "sign=" & set /a ris=%1)
    set ris=0000000%ris%
    set ris=!ris:~-8!
    if "!ris:~0,1!" == "1" (set ris=%sign%1.%ris:~-7%) else (set ris=%sign%0.%ris:~-7%)
  endlocal & set %1=%ris%
goto :eof




Output:

Code: Select all


Angle:(300000/10000)° Integer raw result sin=5000000 cos=8660254

Pad: sin=0.5000000 cos=0.8660254


Einstein1969

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Fastest sin(x) in dos batch

#18 Post by aGerman » 16 Sep 2013 12:42

How can break the for loop in safe mode for use in a macro?

Er ... you want to break the FOR /L loop?
That's not so easy -
infinite loop with break condition

Regards
aGerman

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Fastest sin(x) in dos batch

#19 Post by Aacini » 16 Sep 2013 13:16

... or this one: The ultimate While loop

Antonio

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Fastest sin(x) in dos batch

#20 Post by penpen » 16 Sep 2013 15:25

Another option is the following, but you have to add a label (:endMacro) after any macro call (should be no problem):

Code: Select all

@echo off
cls
setlocal disableDelayedExpansion
call :initMacros

set "N=9"
%$example% N 4 result
:endMacro
echo %N%, 4 %result%
echo =======================
set "N=8"
%$example% N 4
:endMacro

endlocal
goto :eof


:initMacros
set LF=^


::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
:::: StrLen pResult pString
set $example=for /L %%n in (1 1 2) do if %%n==2 (%\n%
    for /F "tokens=1-3 delims=, " %%1 in ("!argv!") do (%\n%
        set "param1=!%%~1!"%\n%
        set "param2=%%~2"%\n%
        set "result=0"%\n%
        set "N=!param1!"%\n%
        for /L %%a in (1,1,!N!) do (%\n%
            if !N! GEQ %%a (%\n%
                set /A "N-=1", "result+=N"%\n%
                echo %%%%a="%%a", N="!N!"%\n%
            ) else (%\n%
                echo ended N loop%\n%
                echo do something after the loop%\n%
                for %%v in (!result!) do endlocal^&(if "%%~3" neq "" (set "%%~3=%%v") else (echo %%v))^&goto :endMacro%\n%
            )%\n%
        )%\n%
        for %%v in (!result!) do endlocal^&(if "%%~3" neq "" (set "%%~3=%%v") else (echo %%v))^&goto :endMacro%\n%
    ) %\n%
) ELSE setlocal enableDelayedExpansion ^& set argv=,

exit /b
The above places the code to execute after the condition in an else case of this condition within the loop you don't want to execute fully.
This can be done multiple times, if you are finished with what you wanted to do, just jump to a label outside the macro using goto.
In case the loop isn't executed you have to add the finishing line to all macro ending possibilities.

penpen

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Fastest sin(x) in dos batch

#21 Post by aGerman » 17 Sep 2013 07:07

You can't break a FOR /L loop via GOTO. You'll see that with ECHO ON

Code: Select all

@prompt $g &setlocal
set "N=10"
for /l %%i in (1 1 %N%) do (
  if %%i==5 goto exitLoop
  echo %%i
)
:exitLoop
pause

It executes the ECHO command only for the first 4 iterations but it doesn't stop the loop.

Regards
aGerman

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Fastest sin(x) in dos batch

#22 Post by penpen » 17 Sep 2013 09:38

Why not? I always have seen this that way:

Code: Select all

@echo off &setlocal
set "N=10"
for /l %%i in (1 1 %N%) do (
:exitLoop
  if %%i==5 goto exitLoop
  echo %%i
)
pause

The goto breaks the loop as it doesn't execute the loops content.
The command line interpreter just have to read the virtual document, that is inserted into the batch file by the for loop.
This is done line by line (or loop body by loop body, default) to not oversee any label.
The label cannot be found within the virtual document, as it is removed on the loop body (on normalization) when building the virtual document, so it doesn't find them within the loop.
But it doesn't execute the code, and goes to the next wanted label, so it is working.
Execution is stopped, that's what is wanted.
You should carefully select the number of iterations, as this extends the virtual document.
So do not insert a document of infinite length, the result were far from beeing optimal.

Edited:
I would agree to you if you meant this: the command line processor doesn't ignore this virtual document, although that should be not difficult to implement (and is done in all other for loops).
Then you would mean the outer script loop that implements the script loop: This loop is indeed not interrupted.

penpen

Edit2: Repositioned the text.
Last edited by penpen on 17 Sep 2013 10:49, edited 2 times in total.

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Fastest sin(x) in dos batch

#23 Post by penpen » 17 Sep 2013 10:44

But you are right aGerman... my above code is not really good... .
As only a very small loop with maximum of 30 iterations is needed, if i see it right,
you may also do it this, more safe, way:

Code: Select all

@echo off
setlocal
set "loop=  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30"



setlocal enableDelayedExpansion
set "size=15"
set /A "loopSize=size*3"

for /F "tokens=* delims=" %%A in ("!loopSize!") do (
   set "looping=!loop:~0,%%A!"
   for %%a in (!looping!) do (
      if %%a == 5 goto :exitLoop
      echo %%a
   )
)
:exitLoop

endlcoal
endlocal
goto :eof
And if you want the macro to compute on just use if else cases as in the above for /L loop scenario, and additionally with goto :endMacro use as above.

penpen

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Fastest sin(x) in dos batch

#24 Post by einstein1969 » 27 Apr 2014 12:58

@penpen:

Thanks penpen,

This work for small loop, i will use if there is necessity!!!
I think that work on nearly ieee 754 if optimized for performance is very good for extending math of dos batch.

@ALL:
Hi, I have rewritten the Maclaurin serie for the Sin(x) function for RADIANT angle because this is more flessible in accord with the wikipedia (english) article.

I have optimized the first, second, and third term. An this is a small library pretty fast.

I know that accessing at lookup table i more fast but in certain cases this method is more fast. Look at pgen demo for an example. The alternative is using a lookup table accessing from file but is not pretty fast how SET /A method.

I ask at experts: Am I right? There'is a faster method?

Code: Select all

::::::::::::::::::::::::::::::::::: TRIGONOMETRIC FUNCTIONS

:: call this function the first time for define some variable: PI=31416, PI_div_2=15708, PIx2=62832, PI32=3/2PI=PI+PI_div_2=47124
:Init_Trig

  set /a "PI=(35500000/113+5)/10, PI_div_2=(35500000/113/2+5)/10, PIx2=2*PI, PI32=PI+PI_div_2"
  set "SIN=(a-a*a/1920*a/312500+a*a/1920*a/15625*a/15625*a/2560000-a*a/1875*a/15360*a/15625*a/15625*a/16000*a/44800000)"

goto :eof


:Sin rad*10000 return_var*10000 RADIANT
  setlocal

    set /a "a=(%1) %% %PIx2%, b=(a>>31|1)*a"
    if !b! gtr %PI32% (set /a "a=a-(a>>31|1)*%PIx2%, a=%SIN%")  else (
      if !b! gtr %PI_div_2% (set /a "a=(a>>31|1)*%PI%-a, a=%SIN%") else set /a "a=%SIN%")
 
  (endlocal & set %2=%a%)
goto :eof


:Cos rad*10000 return_var*10000 RADIANT      Cos(x)=Sin(PI/2-x)
  setlocal

    set /a "a=(%PI_div_2%-%1) %% %PIx2%, b=(a>>31|1)*a"
    if !b! gtr %PI32% (set /a "a=a-(a>>31|1)*%PIx2%, a=%SIN%")  else (
      if !b! gtr %PI_div_2% (set /a "a=(a>>31|1)*%PI%-a, a=%SIN%") else set /a "a=%SIN%")
 
  (endlocal & set %2=%a%)
goto :eof

::::::::::::::::::::::::::::::::::: TRIGONOMETRIC FUNCTIONS END


einstein1969

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Fastest sin(x) in dos batch

#25 Post by einstein1969 » 14 Mar 2016 08:19

Hi,

The faster method developed by Aacini is this:
This version is about EDIT:40% faster (101microseconds in empty env) than previuos.

Code: Select all

  set "_SIN=a-a*a/1920*a/312500+a*a/1920*a/15625*a/15625*a/2560000-a*a/1875*a/15360*a/15625*a/15625*a/16000*a/44800000"

  REM APA mod
  REM http://www.dostips.com/forum/viewtopic.php?f=3&t=6744
  REM                                  if !c! gtr 47124   (set /a "a=a-(a>>31|1)*62832") else if !c! gtr 15708  set /a "a=(a>>31|1)*31416-a"
  set "SIN(x)=(a=(x)%%62832, c=(a>>31|1)*a, t=((c-47125)>>31)+1, a-=t*((a>>31|1)*62832)  +  ^^^!t*( (((c-15709)>>31)+1)*(-(a>>31|1)*31416+2*a)  ), %_SIN%)"
  set "_SIN="


Usage:

Code: Select all

  set /A "sx=%SIN(x):x=45*31416/180%"



I found alternative at !t that is (1-t) and I have not precalculate t but substitute directly.
This version is about 6% faster (95microseconds in empty env!!) than previous.

Code: Select all

  set "SIN(x)=(a=(x)%%62832, c=(a>>31|1)*a, a-=(((c-47125)>>31)+1)*((a>>31|1)*62832)  +  (-((c-47125)>>31))*( (((c-15709)>>31)+1)*(-(a>>31|1)*31416+2*a)  ), %_SIN%)"


The timing is calculated on old Celeron@2Ghz

Einstein1969
Last edited by einstein1969 on 14 Mar 2016 14:37, edited 2 times in total.

foxidrive
Expert
Posts: 6031
Joined: 10 Feb 2012 02:20

Re: Fastest sin(x) in dos batch

#26 Post by foxidrive » 14 Mar 2016 11:38

einstein, can you supply a batch script to test the relative speeds of the two routines, over a set of caluclations?

My thoughts are that I'd be interested to see how a later model PC will render the two routines, compared to your monocore 2 GHz Celeron.

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Fastest sin(x) in dos batch

#27 Post by einstein1969 » 14 Mar 2016 12:28

I use this.
The result is on EDIT:screen. Close any application for stabilize. I don't know if on multicore this is necessary.

Let me know if it works.

For old version:

Code: Select all

@echo off
setlocal DisableDelayedExpansion

Echo Close any application, browser, and suspend process that use CPU.
Echo You can use Resource Monitor under Task Manager , select a process and right click to suspend.
Echo Close Task Manager and Resource Monitor.
Pause

set FPS=if   %%\ geq !\!  (set "FPS_t1=1!time:~-5,-3!!time:~-2!" ^& ((set /p "FPS_old_counter=" ^& set /p "FPS_t0=")^<%tmp%\FPS.$$$.dat.txt) ^& set /a "FPS_dt=FPS_t1-FPS_t0, FPS_check=FPS_dt-1>>31, FPS_rapp=FPS_dt/(FPS_dt+(FPS_check&1)), FPS=(%%\-FPS_old_counter)*10000*100/(FPS_dt+(FPS_check&6000)), \=%%\+6*FPS/100/100+(1-FPS_rapp)*100, FPS_micro=1000000000/(FPS+(FPS-1>>31&1))" ^& Echo FPS=!FPS:~0,-2!.!FPS:~-2! [!FPS_micro:~0,-1!.!FPS_micro:~-1! æs] [#%%\-!\!] ^&set FPS_t1=^&set FPS_old_counter=^&set FPS_t0=^&set FPS_dt=^&set FPS_check=^&set FPS_rapp=^&set FPS=^&set FPS_micro=^& ( (echo %%\)^>%tmp%\FPS.$$$.dat.txt ^& (echo 1!time:~-5,-3!!time:~-2!)^>^>%tmp%\FPS.$$$.dat.txt) )
set INIT_FPS=set \=0^&(echo 0^&echo 1%time:~-5,-3%%time:~-2%)^>%tmp%\FPS.$$$.dat.txt

setlocal EnableDelayedExpansion

%= Put here definition of macro or initialize value =%

  set /a "PI=(35500000/113+5)/10, PI_div_2=(35500000/113/2+5)/10, PIx2=2*PI, PI32=PI+PI_div_2"
  set "SIN=(a-a*a/1920*a/312500+a*a/1920*a/15625*a/15625*a/2560000-a*a/1875*a/15360*a/15625*a/15625*a/16000*a/44800000)"


::BURST MODE ON
(setlocal & for /F "Tokens=1 delims==" %%v in ('set') do set "%%v="

  rem preallocate env. sometimes it works sometimes not. Remove rem for try.
  rem (for /L %%\ in (1,1,2000) do (set z=!z!0000&set z1=!z1!0000)) & set z=&set z1= 

(%INIT_FPS%)
For /L %%\ in (1,1,1600000) do (( For /L %%? in (0,1,99) do (

%= Insert here the code to timing =%

    set /a "a=(45*31416/180) %% %PIx2%, b=(a>>31|1)*a"
    if !b! gtr %PI32% (set /a "a=a-(a>>31|1)*%PIx2%, a=%SIN%")  else (
      if !b! gtr %PI_div_2% (set /a "a=(a>>31|1)*%PI%-a, a=%SIN%") else set /a "a=%SIN%")

    set /A sx=a


  ))
  %FPS%
)

endlocal)
::BURST MODE OFF


goto :eof

On my PC this use 143 Microseseconds.


Last code:

Code: Select all

@echo off
setlocal DisableDelayedExpansion

Echo Close any application, browser, and suspend process that use CPU.
Echo You can use Resource Monitor under Task Manager , select a process and right click to suspend.
Echo Close Task Manager and Resource Monitor.
Pause

set FPS=if   %%\ geq !\!  (set "FPS_t1=1!time:~-5,-3!!time:~-2!" ^& ((set /p "FPS_old_counter=" ^& set /p "FPS_t0=")^<%tmp%\FPS.$$$.dat.txt) ^& set /a "FPS_dt=FPS_t1-FPS_t0, FPS_check=FPS_dt-1>>31, FPS_rapp=FPS_dt/(FPS_dt+(FPS_check&1)), FPS=(%%\-FPS_old_counter)*10000*100/(FPS_dt+(FPS_check&6000)), \=%%\+6*FPS/100/100+(1-FPS_rapp)*100, FPS_micro=1000000000/(FPS+(FPS-1>>31&1))" ^& Echo FPS=!FPS:~0,-2!.!FPS:~-2! [!FPS_micro:~0,-1!.!FPS_micro:~-1! æs] [#%%\-!\!] ^&set FPS_t1=^&set FPS_old_counter=^&set FPS_t0=^&set FPS_dt=^&set FPS_check=^&set FPS_rapp=^&set FPS=^&set FPS_micro=^& ( (echo %%\)^>%tmp%\FPS.$$$.dat.txt ^& (echo 1!time:~-5,-3!!time:~-2!)^>^>%tmp%\FPS.$$$.dat.txt) )
set INIT_FPS=set \=0^&(echo 0^&echo 1%time:~-5,-3%%time:~-2%)^>%tmp%\FPS.$$$.dat.txt

setlocal EnableDelayedExpansion

%= Put here definition of macro or initialize value =%

  set "_SIN=a-a*a/1920*a/312500+a*a/1920*a/15625*a/15625*a/2560000-a*a/1875*a/15360*a/15625*a/15625*a/16000*a/44800000"

  REM APA mod: Convert final sine calculation into a long "function" expression
  REM http://www.dostips.com/forum/viewtopic.php?f=3&t=6744
  REM                                  if !c! gtr 47124   (set /a "a=a-(a>>31|1)*62832") else if !c! gtr 15708  set /a "a=(a>>31|1)*31416-a"
  set "SIN(x)=(a=(x)%%62832, c=(a>>31|1)*a, a-=(((c-47125)>>31)+1)*((a>>31|1)*62832)  +  (-((c-47125)>>31))*( (((c-15709)>>31)+1)*(-(a>>31|1)*31416+2*a)  ), %_SIN%)"
  set "_SIN="


::BURST MODE ON
(setlocal & for /F "Tokens=1 delims==" %%v in ('set') do set "%%v="

  rem preallocate env. sometimes it works sometimes not. Remove rem for try.
  rem (for /L %%\ in (1,1,2000) do (set z=!z!0000&set z1=!z1!0000)) & set z=&set z1= 

(%INIT_FPS%)
For /L %%\ in (1,1,1600000) do (( For /L %%? in (0,1,99) do (

%= Insert here the code to timing =%

  set /A "sx=%SIN(x):x=45*31416/180%"


  ))
  %FPS%
)

endlocal)
::BURST MODE OFF


goto :eof


On my PC this use 95Microseseconds.

Einstein1969
Last edited by einstein1969 on 21 Mar 2016 07:54, edited 2 times in total.

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Fastest sin(x) in dos batch

#28 Post by einstein1969 » 14 Mar 2016 13:21

For better result i use often the 10X instruction like this:

Code: Select all

...
%= Insert here the code to timing =%

  set /A "sx=%SIN(x):x=45*31416/180%"
  set /A "sx=%SIN(x):x=45*31416/180%"
  set /A "sx=%SIN(x):x=45*31416/180%"
  set /A "sx=%SIN(x):x=45*31416/180%"
  set /A "sx=%SIN(x):x=45*31416/180%"
  set /A "sx=%SIN(x):x=45*31416/180%"
  set /A "sx=%SIN(x):x=45*31416/180%"
  set /A "sx=%SIN(x):x=45*31416/180%"
  set /A "sx=%SIN(x):x=45*31416/180%"
  set /A "sx=%SIN(x):x=45*31416/180%"

... 


And divide by 10 the result.

Einstein1969
Last edited by einstein1969 on 14 Mar 2016 14:20, edited 1 time in total.

foxidrive
Expert
Posts: 6031
Joined: 10 Feb 2012 02:20

Re: Fastest sin(x) in dos batch

#29 Post by foxidrive » 14 Mar 2016 13:59

I assume it runs continuously so I copied a few lines - this is the result from the first and second scripts.

Code: Select all

FPS=19189.62 [52.1 µs] [#1485-2636]
FPS=19312.08 [51.7 µs] [#2636-3794]
FPS=19332.22 [51.7 µs] [#3794-4953]


Code: Select all

FPS=2004.04 [498.9 µs] [#100-220]
FPS=1948.05 [513.3 µs] [#220-336]
FPS=1979.52 [505.1 µs] [#336-454]



It doesn't run both sin methods to compare them, does it?
Too early in the morning for me to follow it all. :)

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Fastest sin(x) in dos batch

#30 Post by einstein1969 » 14 Mar 2016 14:43

I have editated the previous posts (last 3). I have correct a value. The performance gain is about 40% with the Aacini version
and total 45% for the last.

Einstein1969

EDIT: Your PC is 2 times fast than mine! on single core!

Post Reply