Page 1 of 4

Fastest sin(x) in dos batch

Posted: 04 Sep 2013 07:41
by einstein1969
hi,

I need to have the sine function in batch. Should be as fast as possible. I only need a few decimal (2-3 or 4) after the decimal point as a result or integer values ​​to be divided. In input also an integer value of degrees or cents / thousandths of degrees.

You could use the mathematics of integers of dos batch to be more 'fast?

Re: Fastest sen(x) in dos batch

Posted: 04 Sep 2013 07:45
by Endoro
please look here.

Re: Fastest sen(x) in dos batch

Posted: 04 Sep 2013 08:01
by einstein1969
I'm sorry, I forgot to add that I would not install anything, so if possible in pure batch dos or with what is available by default.

In that link I could find only two batch but require php.

Re: Fastest sen(x) in dos batch

Posted: 04 Sep 2013 08:42
by einstein1969
I did a test and still does not seem to be very fast.

I cycled about 1000 times and has taken too much on my pc ..

Code: Select all

E:\x264\provini\php>cmd /V:ON /c "echo %time% & ( for /L %# in (1,1,1000) do @(
php.exe -r print(sin("3"^)^); > nul: ) ) & echo !time!"
16:40:08,23
16:40:39,43


about 0.030 sec or 30 ms

Re: Fastest sen(x) in dos batch

Posted: 04 Sep 2013 13:16
by einstein1969
Ok.

I init the skeleton :)

Int_Sinx:

Code: Select all

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:INT_Sinx  Degree  [ResultVAR]
::
::      -- Return the sine for the specified angle in degrees. y=sin(x), where x is in integer
::      
::      -- Degree    [in]    Degree is an integer in [0,90]
::
::      -- ResultVAR    [out]   var reference to return value of y [0,100]
::
::  Version history
::
:: 0.1   4/9/2013   First version that use 2 decimal for result e input in integer degree
::         for angle from 0 to 90 degree.
::
::         From Francesco Poscetti aka Einstein1969
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
SETLOCAL

   REM.--function body
   set /a d=%~1

   if %d% lss 0 echo Not implemented. Exit. & goto :eof
   if %d% gtr 90 echo Not implemented. Exit. & goto :eof

   set /a y=(r=x=314*d/180)-(yp=x*x*x)/6/(o=100)/o+yp*x/120*x/o/o/o/o

(ENDLOCAL & REM -- RETURN VALUES
    IF "%~2" NEQ "" (SET %~2=%y%) ELSE Echo %y%
)
GOTO:EOF
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


For now only first quadrant, the other three are easy

test_code.cmd

Code: Select all

@echo off 
setlocal
setlocal EnableDelayedExpansion

set st=
For /L %%# in (1,1,52) do call set st=#%%st%%

For /L %%# in (0,4,90) do (
   call :Int_Sinx %%# INT_Sinx_result

   set /a "int_sinx_result>>=1"
   call set st_=%%st:~0,-!int_sinx_result!%%

   echo(!st_!
)
For /L %%# in (90,-4,0) do (
   call :Int_Sinx %%# INT_Sinx_result

   set /a "int_sinx_result>>=1"
   call set st_=%%st:~0,-!int_sinx_result!%%

   echo(!st_!
)


Result:

Code: Select all

#################################################
#############################################
##########################################
######################################
###################################
################################
############################
#########################
#######################
####################
#################
###############
#############
###########
#########
#######
######
#####
####
###
##
##
##
##
###
###
####
#####
#######
########
##########
############
##############
################
###################
#####################
########################
###########################
##############################
#################################
#####################################
########################################
############################################
###############################################
###################################################


EDIT: changed for more performance


raw performance sin(45)=0.71:

Code: Select all

E:\x264\provini>cmd /V:ON /c "echo %time% & set degree=45 & ( for /L %# in (1,1,10000
) do @(set /a "y=(r=x=314*degree/180)-(p=x*x*x)/6/(o=100)/o+p*x/120*x/o/o/o/o") >nul
) & echo !time! & if !y! lss 10 (echo 0.0!y!) else if !y! lss 100 (echo 0.!y!) e
lse echo 1.00"
22:49:16,89
22:49:19,87
0.71

3sec/10000= 0.0003s or 0.3ms


.

Re: Fastest sen(x) in dos batch

Posted: 04 Sep 2013 15:49
by penpen
One quite fast solution can be read here: http://lab.polygonal.de/?p=205.
Although this is not done using integer arithmetics, but it may easily be converted to.

penpen

Re: Fastest sen(x) in dos batch

Posted: 05 Sep 2013 06:50
by einstein1969
Thanks penpen the link is very full of trick.

I have tested the fast quadratic version but there is so much error

at 30 degrees the sin(30°)=0,5 but the fast quadratic version is 0,555...

I test quality quadratic version now!

Re: Fastest sen(x) in dos batch

Posted: 05 Sep 2013 18:14
by dbenham
You may think this is cheating, but it certainly is fast, accurate, and pure batch :twisted:

(I did use JScript to initially get the lookup values - not shown)

It provides an accurate sin() value for any integral number of degrees, with 5 decimal digits of precision. The 5 was an arbitrary choice.

An extremely fast direct lookup is used for values between 0 and 359: 0.09 msec on my machine.

A macro is used for any other integral value: 0.81 msec on my machine

I can't figure out why the macro "call" must be within parentheses :?:

Code: Select all

@echo off
setlocal disableDelayedExpansion

:: =========================== Begin Initialization ================================

set LF=^


set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"

set ^"sin=%\n%
for %%# in (1 2) do if %%#==2 (for /f "tokens=1,2" %%1 in ("!args!") do (%\n%
  set /a "N=%%1%%360"%\n%
  if !N! lss 0 set /a "N+=360"%\n%
  for %%N in (!N!) do for %%n in (!sin%%N!) do (%\n%
    endlocal%\n%
    if "%%2" equ "" (echo %%n) else set "%%2=%%n"%\n%
  )%\n%
)) else setlocal enableDelayedExpansion^&set args=^"

setlocal enableDelayedExpansion
set N=0
for %%V in (
0.00000 0.01745 0.03490 0.05234 0.06976 0.08716 0.10453 0.12187 0.13917 0.15643
0.17365 0.19081 0.20791 0.22495 0.24192 0.25882 0.27564 0.29237 0.30902 0.32557
0.34202 0.35837 0.37461 0.39073 0.40674 0.42262 0.43837 0.45399 0.46947 0.48481
0.50000 0.51504 0.52992 0.54464 0.55919 0.57358 0.58779 0.60182 0.61566 0.62932
0.64279 0.65606 0.66913 0.68200 0.69466 0.70711 0.71934 0.73135 0.74314 0.75471
0.76604 0.77715 0.78801 0.79864 0.80902 0.81915 0.82904 0.83867 0.84805 0.85717
0.86603 0.87462 0.88295 0.89101 0.89879 0.90631 0.91355 0.92050 0.92718 0.93358
0.93969 0.94552 0.95106 0.95630 0.96126 0.96593 0.97030 0.97437 0.97815 0.98163
0.98481 0.98769 0.99027 0.99255 0.99452 0.99619 0.99756 0.99863 0.99939 0.99985
1.00000 0.99985 0.99939 0.99863 0.99756 0.99619 0.99452 0.99255 0.99027 0.98769
0.98481 0.98163 0.97815 0.97437 0.97030 0.96593 0.96126 0.95630 0.95106 0.94552
0.93969 0.93358 0.92718 0.92050 0.91355 0.90631 0.89879 0.89101 0.88295 0.87462
0.86603 0.85717 0.84805 0.83867 0.82904 0.81915 0.80902 0.79864 0.78801 0.77715
0.76604 0.75471 0.74314 0.73135 0.71934 0.70711 0.69466 0.68200 0.66913 0.65606
0.64279 0.62932 0.61566 0.60182 0.58779 0.57358 0.55919 0.54464 0.52992 0.51504
0.50000 0.48481 0.46947 0.45399 0.43837 0.42262 0.40674 0.39073 0.37461 0.35837
0.34202 0.32557 0.30902 0.29237 0.27564 0.25882 0.24192 0.22495 0.20791 0.19081
0.17365 0.15643 0.13917 0.12187 0.10453 0.08716 0.06976 0.05234 0.03490 0.01745
0.00000 -0.01745 -0.03490 -0.05234 -0.06976 -0.08716 -0.10453 -0.12187 -0.13917 -0.15643
-0.17365 -0.19081 -0.20791 -0.22495 -0.24192 -0.25882 -0.27564 -0.29237 -0.30902 -0.32557
-0.34202 -0.35837 -0.37461 -0.39073 -0.40674 -0.42262 -0.43837 -0.45399 -0.46947 -0.48481
-0.50000 -0.51504 -0.52992 -0.54464 -0.55919 -0.57358 -0.58779 -0.60182 -0.61566 -0.62932
-0.64279 -0.65606 -0.66913 -0.68200 -0.69466 -0.70711 -0.71934 -0.73135 -0.74314 -0.75471
-0.76604 -0.77715 -0.78801 -0.79864 -0.80902 -0.81915 -0.82904 -0.83867 -0.84805 -0.85717
-0.86603 -0.87462 -0.88295 -0.89101 -0.89879 -0.90631 -0.91355 -0.92050 -0.92718 -0.93358
-0.93969 -0.94552 -0.95106 -0.95630 -0.96126 -0.96593 -0.97030 -0.97437 -0.97815 -0.98163
-0.98481 -0.98769 -0.99027 -0.99255 -0.99452 -0.99619 -0.99756 -0.99863 -0.99939 -0.99985
-1.00000 -0.99985 -0.99939 -0.99863 -0.99756 -0.99619 -0.99452 -0.99255 -0.99027 -0.98769
-0.98481 -0.98163 -0.97815 -0.97437 -0.97030 -0.96593 -0.96126 -0.95630 -0.95106 -0.94552
-0.93969 -0.93358 -0.92718 -0.92050 -0.91355 -0.90631 -0.89879 -0.89101 -0.88295 -0.87462
-0.86603 -0.85717 -0.84805 -0.83867 -0.82904 -0.81915 -0.80902 -0.79864 -0.78801 -0.77715
-0.76604 -0.75471 -0.74314 -0.73135 -0.71934 -0.70711 -0.69466 -0.68200 -0.66913 -0.65606
-0.64279 -0.62932 -0.61566 -0.60182 -0.58779 -0.57358 -0.55919 -0.54464 -0.52992 -0.51504
-0.50000 -0.48481 -0.46947 -0.45399 -0.43837 -0.42262 -0.40674 -0.39073 -0.37461 -0.35837
-0.34202 -0.32557 -0.30902 -0.29237 -0.27564 -0.25882 -0.24192 -0.22495 -0.20791 -0.19081
-0.17365 -0.15643 -0.13917 -0.12187 -0.10453 -0.08716 -0.06976 -0.05234 -0.03490 -0.01745
) do (
  set "sin!N!=%%V"
  set /a "N+=1"
)

:: =========================== End Initialization ================================

:: Use direct lookup if angle is known to be an integer between 0 and 359 inclusive
for %%N in (0 45 90 135 180 225 270 315) do echo sin(%%N^) = !sin%%N!

:: Use macro if angle may be negative or >= 360
for %%N in (-45 405) do (
  (%sin% %%N N)
  echo sin(%%N^) = !N!
)

echo(
echo Speed test direct lookup between 0 and 359
echo %time%
for /l %%N in (0 1 10000) do set "N=!sin180!"
echo %time%

echo(
echo Speed test using macro
echo %time%
for /l %%N in (0 1 10000) do (%sin% %%N N)
echo %time%
=== OUTPUT ===

Code: Select all

sin(0) = 0.00000
sin(45) = 0.70711
sin(90) = 1.00000
sin(135) = 0.70711
sin(180) = 0.00000
sin(225) = -0.70711
sin(270) = -1.00000
sin(315) = -0.70711
sin(-45) = -0.70711
sin(405) = 0.70711

Speed test direct lookup between 0 and 359
20:02:53.16
20:02:54.12

Speed test using macro
20:02:54.12
20:03:02.22


Dave Benham

Re: Fastest sen(x) in dos batch

Posted: 06 Sep 2013 16:24
by einstein1969
Well what to say? it's amazing what you can do!

The use of the macro is brilliant and incomprehensible to me. The code is clearly legible and easy to use.

How can I use a macro in my function? :?

In the meantime, I wrote a new version that has nothing to do with the final version.

The question I asked myself when I read the link posted by penpen is just the limit of the memory space of the variables, so I discarded the version with quick lookup tables because the input function will also have tenths, hundredths and thousandths of a degree.

Thank you very much dbenham

The new version. More precise e more fast


Code: Select all

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:INT_SinD  Degree  [ResultVAR]
::
::      -- Return the sine for the specified angle in degrees. y=sin(x), where x is in integer
::      
::      -- Degree    [in]    Degree is an integer in [0,90]
::
::      -- ResultVAR    [out]   var reference to return value of y [0,100]
::
::
::  Version history
::
:: 0.2  6/9/2013   
::         1) Improved precision using sine/cosine property
::         2) optimization of integer use and new formula usage
::         3) changhed name for "degree" input
::         4) implemented "round" function for more precision, internal work on 3 digit
::
:: 0.1   4/9/2013   
::         First version that use 2 decimal for result e input in integer degree
::         for angle from 0 to 90 degree.
::
::         From Francesco Poscetti aka Einstein1969
::
::         This Function allow you to calculate almost all other common trigonometric
::         function. Use the Taylor/Maclaurin function.
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
SETLOCAL

   REM.--function body
   set /a "d=%~1"

   if %d% lss 0 echo Not implemented. Exit. & goto :eof
   if %d% gtr 90 echo Not implemented. Exit. & goto :eof

   if %d% leq 48 (

      set /a "y=((x=3141592*d/180/(o=1000))-x*x*x/6/(o*o)+5)/10"

   ) else (

      set /a "y=((o=1000)-(x=3141592*(90-d)/180/o)*x/2/o+5)/10"

   )

(ENDLOCAL & REM -- RETURN VALUES
    IF "%~2" NEQ "" (SET %~2=%y%) ELSE Echo %y%
)
GOTO:EOF
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


Einstein1969

Re: Fastest sen(x) in dos batch

Posted: 06 Sep 2013 17:26
by aGerman
How can I use a macro in my function? :?

Try:

Code: Select all

@echo off &setlocal

set LF=^


set ^"\n=^^^%LF%%LF%^%LF%%LF%^^" &REM Two empty lines required!
set sin==for %%i in (1 2) do if %%i==2 (%\n%
  for /f "tokens=1,2" %%j in ("!argv!") do (set /a "d=%%~j" ^& set "ret=%%~k")%\n%
  if !d! geq 0 (%\n%
    if !d! leq 90 (%\n%
      if !d! leq 48 (%\n%
        set /a "y=((x=3141592*d/180/(o=1000))-x*x*x/6/(o*o)+5)/10"%\n%
      ) else (%\n%
        set /a "y=((o=1000)-(x=3141592*(90-d)/180/o)*x/2/o+5)/10"%\n%
      )%\n%
      for /f "tokens=1,2" %%j in ("!y! !ret!") do (%\n%
        ENDLOCAL%\n%
        if "%%k" NEQ "" (SET %%k=%%j) ELSE Echo %%j%\n%
      )%\n%
    ) else (%\n%
      ENDLOCAL%\n%
      echo Not implemented.%\n%
    )%\n%
  ) else (%\n%
    ENDLOCAL%\n%
    echo Not implemented.%\n%
  )%\n%
) else SETLOCAL EnableDelayedExpansion ^& set argv=

:::::::::::::::::::::::::

%sin% 0

%sin% 45

%sin% 90 result
echo %result%

pause

Regards
aGerman

Re: Fastest sen(x) in dos batch

Posted: 07 Sep 2013 07:53
by einstein1969
thanks a lot, aGerman!

It seems to work fine. Can I ask why there are all those endlocal?

However, I baked the new version rather complete and running, however, still to be optimized for better performance.

Code: Select all

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:INT_SinD  Degree  [ResultVAR]
::
::      -- Return the sine for the specified angle in degrees.
::         y=INT_sinD(x), where x is in integer and y is an integer
::         y is the integer (rounded) part of sine moltiply 100.
::      
::      -- Degree      [in]    Degree is an integer in [-2147483647,2147483647]
::
::      -- ResultVAR    [out]   var reference to return value of y [-100,100]
::
::
::  Version history
::
:: 0.3  7/9/2013
::         1) improved precision. Error max=0.0052
::         2) implemented for range in [-2147483647,2147483647] , return [-100,100]
::
:: 0.2  6/9/2013   
::         1) Improved precision using sine/cosine property Sin(x)=...
::         2) optimization of integer use and new formula usage
::         3) changhed name for "degree" input
::         4) implemented "round" function for more precision, internal work on 3 digit
::
:: 0.1   4/9/2013   
::         First version that use 2 decimal for result e input in integer degree
::         for angle from 0 to 90 degree.
::
::         From Francesco Poscetti aka Einstein1969
::
::         NOTE: This Function allow you to calculate almost all other common
::         trigonometric function. Uses the Taylor/Maclaurin function.
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Setlocal

   REM.--function body

   set /a "d=%~1 %% 360"

   if %d% lss 0   ( set /a d=-d, rr=-1 ) else ( set /a rr=1 )
   if %d% geq 180 ( set /a d=d-180, rr=-rr )
   if %d% gtr 90  ( set /a d=180-d)

   if %d% leq 45 (

      set /a "y=(((x=3141592*d/180/(o=1000))-x*x*x/6/(o*o))+5)/10*rr"

   ) else (

      set /a "y=(((o=1000)-(x=3141592*(90-d)/180/o)*x/2/o+x*x*x/100/2*x/12/(o*o)/10)+4)/10*rr"      

   )

(Endlocal & REM -- Return Values
    If "%~2" neq "" (Set %~2=%y%) Else Echo %y%
)
Goto:EOF
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


EDIT: There where !d! ... replaced with %d%

Einstein1969

Re: Fastest sen(x) in dos batch

Posted: 07 Sep 2013 08:22
by aGerman
Can I ask why there are all those endlocal?

The only way to pass arguments to the macro is the SET statement in the ELSE branch at the end of the macro. As you can see there is also a SETLOCAL EnableDelayedExpansion that you need to expand variables inside of the command line block in the associated IF branch.
Because of all the other IF statements you need an ENDLOCAL at each point where the macro execution could end up. Otherwise the delayed variable expansion is still valid for the rest of your code (with all its side effects for strings that include exclamation marks).

Regards
aGerman

Re: Fastest sen(x) in dos batch

Posted: 08 Sep 2013 13:59
by Aacini
Some time ago I used a method to perform operations with SIN(x) over degree values (0-360) using only integer operations, that is to say, I always used the result of SIN(x) to multiply it by an integer value and get an integer as result. I used the same method in a Batch program later that I posted in this post (this part appear after 6-CursorPos.exe.hex program):
Aacini wrote:We may even draw trigonometric functions if we define a table of SIN(x) values multiplied by a standard factor (ie: 0xFFFF or 65535):

This way, to get SIN(x) multiplied by a number, just multiply the SIN[%x%] table value by the number and shift the result 16 bits to the right. This method correctly works with signed values because SET /A right shift operation is an aritmethic one (SAR), although the documentation indicate that is "logical shift" (SHR).

For example:

Code: Select all

@echo off 
setlocal EnableDelayedExpansion

call :DefineSinTable

set st=
For /L %%i in (1,1,52) do set st=#!st!

For /L %%x in (0,4,90) do (
   set /a "int_sinx_result=(SIN[%%x]*52)>>16"
   call set st_=%%st:~0,-!int_sinx_result!%%
   echo(!st_!
)

For /L %%x in (90,-4,0) do (
   set /a "int_sinx_result=(SIN[%%x]*52)>>16"
   call set st_=%%st:~0,-!int_sinx_result!%%
   echo(!st_!
)
goto :EOF


:DefineSinTable

rem Definition of SIN table values (SIN(x)*65535) for 0-360 degrees
rem Antonio Perez Ayala

set Quad1=0
for %%a in ( 1144  2287  3430  4572  5712  6850  7987  9121 10252 11380 12505 13626 14742 15855 16962
            18064 19161 20252 21336 22415 23486 24550 25607 26656 27697 28729 29753 30767 31772 32768
            33754 34729 35693 36647 37590 38521 39441 40348 41243 42126 42995 43852 44695 45525 46341
            47143 47930 48703 49461 50203 50931 51643 52339 53020 53684 54332 54963 55578 56175 56756
            57319 57865 58393 58903 59396 59870 60326 60764 61183 61584 61966 62328 62672 62997 63303
            63589 63856 64104 64332 64540 64729 64898 65048 65177 65287 65376 65446 65496 65526 65535
           ) do (
   set /A Quad1+=1, Quad2=180-Quad1, Quad3=180+Quad1, Quad4=360-Quad1
   set SIN[!Quad1!]=%%a
   set SIN[!Quad2!]=%%a
   set SIN[!Quad3!]=-%%a
   set SIN[!Quad4!]=-%%a
)
for %%a in (0 180 360) do set SIN[%%a]=0
exit /B

Result:

Code: Select all

#################################################
#############################################
##########################################
######################################
###################################
###############################
############################
#########################
######################
###################
################
##############
############
#########
#######
######
####
###
##
#
#
#
#
#
#
##
###
####
#####
#######
########
##########
#############
###############
##################
####################
#######################
##########################
##############################
#################################
####################################
########################################
###########################################
###############################################
###################################################

Antonio

Re: Fastest sen(x) in dos batch

Posted: 10 Sep 2013 03:08
by einstein1969
@aGerman

Thank you very much for the explanation aGerman. In the new version I have included the use of expanded variables directly,
then the approach should be different? The code of the new version will come soon.


@Aacini
Aacini I appreciate your intervention. I wanted to ask an explanation of the method used by you to maintain information of the decimal. The number of 16-bit has been chosen as a criterion?

Re: Fastest sen(x) in dos batch

Posted: 10 Sep 2013 20:34
by Aacini
einstein1969 wrote:@Aacini
Aacini I appreciate your intervention. I wanted to ask an explanation of the method used by you to maintain information of the decimal. The number of 16-bit has been chosen as a criterion?

The original method was used in an assembly language program that calculate a series of (x,y) points to draw circles and ellipses via drawpoly BGI function. The radius was a 16-bits number that was multiplied by the value of the Sin table giving the 32-bits result in two separated 16-bits registers, so I directly took the high-order register of the result with no additional operation. This method was at least as fast as the original circle BGI function!

The 16-bits Sin(x) table, (Sin(x)*65535 as unsigned 16-bits value, equivalent to a little less than 5 decimal digits, but more than 4) was enough to draw perfect circles in the screen no matter the radius. If you need more precision, you may use more bits for the Sin(x) table and less for the factor. For example, you may use 24 bits for the Sin(x) table (Sin(x)*16777215, equivalent to more than 7 decimal digits) and 7 bits for the factor (a maximum value of 127), and perform the operation this way: set /A result=(SIN[!x!]*factor)>>24.

Antonio