Fast sincos routine

O

OcelotIguana

Hello,

Does anyone have any suggestions for where to find a good sincos
routine (i.e. a routine that calculates both the sin and cos of a
given argument) written in c?

Thanks,

(e-mail address removed)
 
J

Julian V. Noble

OcelotIguana said:
Hello,

Does anyone have any suggestions for where to find a good sincos
routine (i.e. a routine that calculates both the sin and cos of a
given argument) written in c?

Thanks,

(e-mail address removed)

Do you have a math co-processor available or does it have to be software?
If the former, they mostly have the function built in, so you had best
do it in assembler.

If you want to do it entirely in high-level C, use the following scheme:

1. Reduce the angle x to the range (0, pi/2)

2. Let z = tan(x/2),
then sin(x) = 2*z/(1+z*z), cos(x) = (1-z*z)/(1+z*z) .

3. Compute z using the continued fraction representation, with w = u*u :

tan(u) = u/(1-w/(3-w/(5-w/(7-...) ) ) )

The only problem with continued fractions is that each step requires
a division, which on Pentia is much slower than a multiply.

If your system already has tan(u), you are home free: compute tan(x/2)
and then sine and cosine with 2 divisions, 2 multiplications and 2 additions.
The arithmetic will take negligible time relative to evaluating the tangent.


--
Julian V. Noble
Professor Emeritus of Physics
(e-mail address removed)
^^^^^^^^^^^^^^^^^^
http://galileo.phys.virginia.edu/~jvn/

"God is not willing to do everything, and thereby take away our free wiil
and that share of glory that rightfully belongs to ourselves."


-- N. Machiavelli, "The Prince".
 
C

CBFalconer

OcelotIguana said:
Does anyone have any suggestions for where to find a good sincos
routine (i.e. a routine that calculates both the sin and cos of a
given argument) written in c?

void sincos(const double x, double *sinval, double *cosval)
{
*sinval = sin(x);
*cosval = sqrt(1.0 - *sinval * *sinval);
}
 
L

Larry Doolittle

void sincos(const double x, double *sinval, double *cosval)
{
*sinval = sin(x);
*cosval = sqrt(1.0 - *sinval * *sinval);
}

If you need perfect accuracy, and/or your computer has good floating
point hardware, that routine will be hard to beat. For merely good
accuracy, and with integer-only hardware, you need a CORDIC algorithm,
which is not normally part of the standard library. I have used
Ken Turkowski's copy, which he has posted at
http://www.worldserver.com/turk/opensource/Cordic.c.txt
It doesn't take many changes [*] to get it to compile with
gcc -std=c99 -pedantic -W -Wall -Wredundant-decls -Wpointer-arith \
-Wcast-qual -Wshadow -O2
For embedded work with a StrongARM, this saved me a bucket of CPU cycles.

- Larry

[*] If you want a copy of my patches, e-mail me.
 
E

Eric Sosman

CBFalconer said:
void sincos(const double x, double *sinval, double *cosval)
{
*sinval = sin(x);
*cosval = sqrt(1.0 - *sinval * *sinval);
}

Alas, this tells me that the cosine of pi (180 degrees)
is positive, when any schoolchild knows it's negative.
 
E

E. Robert Tisdale

Eric said:
Alas, this tells me that the cosine of pi (180 degrees) is positive,
when any schoolchild knows it's negative.

As any honors student
in Mathematics and Physics at McGill University should know. :)
 
A

Allan Bruce

Eric Sosman said:
Alas, this tells me that the cosine of pi (180 degrees)
is positive, when any schoolchild knows it's negative.

but sqrt of a positive number can be positive or negative

e.g. sqrt(4) = +/- 2
 
E

E. Robert Tisdale

Allan said:
but sqrt of a positive number can be positive or negative

e.g. sqrt(4) = +/- 2

But the result of the standard C library function sqrt(double) cannot.
 
M

Mark McIntyre

but sqrt of a positive number can be positive or negative

e.g. sqrt(4) = +/- 2

True, but useless. sqrt() doesn't return a vector, or even a -ve value..
 
E

E. Robert Tisdale

Allan said:
It's not difficult to implement though to get the correct answer.

This is the comp.lang.c newsgroup.

Please show us the implementation in C.
 
C

CBFalconer

E. Robert Tisdale said:
This is the comp.lang.c newsgroup.
Please show us the implementation in C.

You are not overly intelligent, are you?

negroot = -(posroot = sqrt(x));
 
A

Allan Bruce

CBFalconer said:
You are not overly intelligent, are you?

negroot = -(posroot = sqrt(x));

Thank you. I thought he was taking the mickey asking for an implementation!
Allan
 
D

Dik T. Winter

>
> but sqrt of a positive number can be positive or negative
>
> e.g. sqrt(4) = +/- 2

Not in mathematics or in C. The above is valid for x in [-pi/2,pi/2],
but a more precise implementation is:
*cosval = sqrt((1.0 - *sinval) * (1.0 + *sinval));
 
E

Eric Sosman

CBFalconer said:
You are not overly intelligent, are you?

negroot = -(posroot = sqrt(x));

Good, but not yet good enough. One additional bit
of information needs to be computed: Which of `posroot'
and `negroot' is *the* cosine of `x'?

One way to choose the sign would be to use fmod()
to determine how many multiples of pi/2 are present, but
it's rather difficult to form an exact representation of
pi/2 in C's native arithmetic types ... As Plauger says
somewhere in "The Standard C Library," the library's
trig functions are likely to do a better job than the
caller can of throwing out extraneous multiples of pi.
(Elsewhere, this has been called "pi throwing.") The
upshot is that the library's best answer to the O.P.'s
request is probably

void sincos(double x, double *psin, double *pcos) {
*psin = sin(x);
*pcos = cos(x);
}

.... which I imagine the O.P. would find unsatisfactory.
 
T

Tim Prince

Eric Sosman said:
CBFalconer wrote:
The
upshot is that the library's best answer to the O.P.'s
request is probably

void sincos(double x, double *psin, double *pcos) {
*psin = sin(x);
*pcos = cos(x);
}

... which I imagine the O.P. would find unsatisfactory.
Unless using a compiler which automatically optimizes this combination.
Another possibility, which political correctness probably forbids
mentioning, is to look up the specific capabilities of the platform in use.
 
O

OcelotIguana

Thanks to everyone who has posted in response to my original message.

Perhaps I should clarify what I am asking. I have a suite of
numerical codes that I recently profiled and found that the standard
sin and cos routines are a huge percentage of my total run time (which
is many days). A colleague told me that the standard math libraries
are optimized for size, not speed, and that since I always call sin
and cos of the same argument, I should look for a speed optimized
sincos routine which shouldn't take much more time than either a sin
or cos take individually while maintaining the same level of accuracy.
BTW, I'm using Borland C++ Builder 6.0 on a Pentium IV in Win2k.

Ideally, this fast sincos would be something already written that has
been speed optimized (without sacrificing accuracy) by people much
smarter about this issue than myself...

Thanks again,

(e-mail address removed)
 
C

Christian Bau

Thanks to everyone who has posted in response to my original message.

Perhaps I should clarify what I am asking. I have a suite of
numerical codes that I recently profiled and found that the standard
sin and cos routines are a huge percentage of my total run time (which
is many days). A colleague told me that the standard math libraries
are optimized for size, not speed, and that since I always call sin
=============================

Usually they are optimised for accuracy.
and cos of the same argument, I should look for a speed optimized
sincos routine which shouldn't take much more time than either a sin
or cos take individually while maintaining the same level of accuracy.
BTW, I'm using Borland C++ Builder 6.0 on a Pentium IV in Win2k.

Have you looked at reducing the number of calls to sin and cos? For
example, consecutive values of sin (a + k * b), for k = 0, 1, 2, 3, etc.
can be calculated very easily with a single multiplication and addition.
Anything doing 3D graphics can usually be done with hardly any
trigonometric functions at all.

Do you have values that are very close together?

sin (x + eps) = sin (x) * cos (eps) + sin (eps) * cos (x)
cos (x + eps) = cos (x) * cos (eps) - sin (eps) * sin (x)

(You better check these)

If eps is small enough then you can replace cos (eps) with 1, sin (eps)
with eps and get

sin (x + eps) = sin (x) + eps * cos (x)
cos (x + eps) = cos (x) - eps * sin (x)

Grab the source of an existing implementation of sin and cos. They all
do two steps, for example for sin (x):

Step 1: Given x, find k such that abs (x - k * pi/2) <= pi/4.
Step 2: Let y = x - k * pi/2.
Step 3: Calculate one of sin(y), cos (y), -sin(y), -cos(y),
depending on the last two bits of k, using a polynomial.

By calculating sin and cos simultaneously, you know both will have the
same k and y. You also will have to calculate both sin(y) and cos(y)
using two polynomials, then just pick the right results and apply the
sign. So you win by just merging two such implementations.

If you have many calls, chances are the arguments are close together, so
many consecutive arguments will use the same value k. Try writing a
vectorised function:

void vec_sincos (double s[], double c[], double x[], size_t n);

where you will lose lots of the overhead and give the compiler a chance
of optimising.

BTW. Profilers have been known to lie, especially for small function
calls. Just write a test program that does a billion calls to sin and
cos, profile it, and compare the results with stopwatch results to make
sure you are not going down the wrong path.
 
J

Julian V. Noble

OcelotIguana said:
Thanks to everyone who has posted in response to my original message.

Perhaps I should clarify what I am asking. I have a suite of
numerical codes that I recently profiled and found that the standard
sin and cos routines are a huge percentage of my total run time (which
is many days). A colleague told me that the standard math libraries
are optimized for size, not speed, and that since I always call sin
and cos of the same argument, I should look for a speed optimized
sincos routine which shouldn't take much more time than either a sin
or cos take individually while maintaining the same level of accuracy.
BTW, I'm using Borland C++ Builder 6.0 on a Pentium IV in Win2k.

Ideally, this fast sincos would be something already written that has
been speed optimized (without sacrificing accuracy) by people much
smarter about this issue than myself...

Thanks again,

(e-mail address removed)

If you don't need lots of precision, you can use table lookup. (basically,
you hash into the tables) as follows: suppose your basic range is

0 to +pi/4 = 45 deg ;

divide that range into--say--91 steps of 0.5 deg, as in Abramowitz & Stegun's
tables. The first thing you do is fill the sin and cos tables. Then, after
converting the angle to fit in the range, multiply by an appropriate factor
(2 in this case) and extract the integer part to get an integer in the range
0 to 89, call it k, plus a fraction x between 0 and 1. Then you evaluate

sin(k) * (1-x) + sin(k+1) * x

and

cos(k) * (1-x) + cos(k+1) * x

for linear interpolation. On modern machines it is best to increase the
number of stored values to increase precision, rather than performing an
interpolation of higher degree. (You are trading space for speed, as usual.)


Julian V. Noble
Professor Emeritus of Physics
(e-mail address removed)
^^^^^^^^
http://galileo.phys.virginia.edu/~jvn/

"For there was never yet philosopher that could endure the toothache
patiently." -- Wm. Shakespeare, Much Ado about Nothing. Act v. Sc. 1.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,216
Latest member
topweb3twitterchannels

Latest Threads

Top