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
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
(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