Rob van der Woude's Scripting Pages

Calculate cosines in batch

Do not take this too seriously, but I just wanted to show how NT batch files can be used for complex math by cleverly splitting up the math.

I used a Taylor series for the cosine function, and had to add a lot more code to overcome the severe limit of (32-bit) integer only math in batch:

cos(x) = 1 - x^2/2! + x^4/4! - x^6/6! _ x^8/8! ...

cos(x) = 1 - x2/2! + x4/4! - x6/6! - x8/8! ...

This calculation can be optimized by using the facts that:

  1. x2=x2*1, x4=x2*x2, x6=x2*x4, x8=x2*x6, etcetera
  2. 2!=2*1*1, 4!=4*3*2!, 6!=6*5*4!, 8!=8*7*6!, etcetera

The hard part was finding the optimal number of digits to use: too many would soon result in an overflow (batch math is limited to 32-bit integers), too few would degrade accuracy of each factor.

Well, I wouldn't bet on accuracy anyway, for accurate cosine calculation use a "real" language. I recently wrote some simple PHP based batch files, one of them to calculate cosines.

Still, it was fun to write this one that uses SET only for its calculations:

@ECHO OFF
:: A not-too-serious attempt to calculate cosines using native batch
:: commands only by Rob van der Woude, http://www.robvanderwoude.com

:: Usage:  COSINE.BAT  angle

:: Angle must be specified in radians

:: Accuracy deteriorates fast with larger angles

:: cos(x) = 1 - x2/2! + x4/4! - x6/g! + x8/8! - ...
:: cos(x) = 1 - x2/2! + (x2/2!)*x2/(3*4) - (x4/4!)*x2/(5*6) + (x6/6!)*x2/(7*8) - ...
:: 100 * cos(x) = 100 - (100)*((100x)2/(100)2)/2! + (100*((100x)2/(100)2)/2!)*(100x)2/(3*4*(100)2) - ...


SETLOCAL ENABLEDELAYEDEXPANSION

:: pi/4 = 0,78539816339744830961566084581988
SET X=0.78539816339744830961566084581988
IF NOT "%~1"=="" SET X=%~1

:: The following sector converts the floating point value to
:: an integer of 10000 times its value (correctly rounded);
:: a lot of work to compensate for the lack of floating point
:: math in batch

:: Accuracy specifies how many digits will be used
SET Accuracy=3
SET Factor0=1
FOR /L %%A IN (1,1,%Accuracy%) DO SET /A Factor0 *= 10

ECHO.%x% | FIND "." >NUL
IF ERRORLEVEL 1 (
	SET /A  NewX = !X:~0,%Accuracy%! * %Factor0%
) ELSE (
	SET DotPos=
	SET /A NewDotPos = 10 + %Accuracy% + 1
	SET X=%X%000000000000
	FOR /L %%A IN (0,1,10) DO (
		IF NOT DEFINED DotPos (
			IF "!X:~%%A,1!"=="." (
				SET DotPos=%%A
			)
		)
	)
	SET /A NewDotPos = !DotPos! + %Accuracy% + 2
)
IF DEFINED NewDotPos (
	SET NewX=!X:~0,%NewDotPos%!
	SET NewX=!NewX:.=!
	SET /A NewX = "( 1!NewX! + 5 - ( 100 * %Factor0% ) ) / 10"
)

SET Cosine=%Factor0%
SET PrevFactor=%Factor0%
SET Sign=1
SET /A NewX2 = %NewX% * %NewX%

FOR /L %%A IN (2,2,12) DO (
	SET /A Sign *= -1
	IF !PrevFactor! LEQ 0 (
		SET PrevFactor=0
		SET Factor%%A=
	) ELSE (
		SET /A Factor%%A = "( !PrevFactor! * !NewX2! / %Factor0% ) / ( ( %%A - 1 ) * %%A * %Factor0% )"
		IF !Factor%%A! LEQ 0 (
			SET PrevFactor=0
			SET Factor%%A=
		) ELSE (
			SET /A Cosine = "!Cosine! + !Sign! * !Factor%%A!"
			SET PrevFactor=!Factor%%A!
		)
	)
)

IF %Cosine% LSS %Factor0% (
	SET Cosine=0000%Cosine%
	SET Cosine=0.!Cosine:~-%Accuracy%!
) ELSE (
	SET Cosine=1
)
ECHO cosˆ(%~1ˆ)=%Cosine%

ENDLOCAL

page last modified: 2011-11-07; loaded in 0.0038 seconds