Quarter Life Crisis

The world according to Sven-S. Porst

« Taylor SeriesMainFerns and Fern Allies »

Sine Computations

2014 words on

As a follow-up to yesterday’s look at Taylor Series and wondering whether or not they are used to compute trigonometric functions on actual system, here come a few sine computations. We try to compute the values of sin(x), where x are various powers of 10 on different hardware and software. Each of the hardware, operating system and runtime environment can influence the results.

To have a good start let’s try Mathematica (version 4.2, Mac OS X, PowerPC) first for some reference values. Using the N[] command it should give results with arbitrary precision. Evaluating several expressions up to 30 decimal digits, we got:

sin(1): 0.8414709848078965 06652502321630
sin(10): -0.5440211108893698 13404747661851
sin(100): -0.50636564110975879 3656557610460
sin(1000): 0.8268795405320025 60255887429109
sin(10000): -0.3056143888882521 41360910035233
sin(100000): 0.03574879797201650 93164705006958
sin(100000000): 0.9316390271097260 08027516653612
sin(1000000000): 0.545843449448699 564244387270898
sin(1050): -0.789672493429310 082710289539917
sin(100000000000000007629769841091887003294964970946560): -0.4805001434937588 25227634749597

The values we have are for various powers of 10. For the 1050 case at the end, we also compute the value for the double type number used to represent that power – which is quite a bit off and highlights that you shouldn’t even try to compute sines of such large numbers with such low precision number representations.

With the remaining computations using floating point numbers we have to expect the results to differ from Mathematica’s in the last few digits because of those numbers’ limitation in precision. I inserted a space after 15 or 16 decimal places, which is the amount of precision we can expect from computations with double type numbers. In the later results, a space is inserted before the first digit in which results differ from these digits. Bold face is used to indicate the digits in those results which are wrong.

It will, however, be interesting to note the differences between those results which mostly suggest that the standard maths library on 32 bit Intel systems isn’t a bastion of precision for these computations.

Intel Core2Duo, Mac OS X.5

I.e. all 64bit on the Mac, running a C tool using math.h’s sin() function.

sin(1): 0.8414709848078965 0488
sin(10): -0.5440211108893698 8548
sin(100): -0.50636564110975879 061
sin(1000): 0.8268795405320026 3260
sin(10000): -0.3056143888882521 5310
sin(100000): 0.03574879797201650 777
sin(100000000): 0.9316390271097260 1451
sin(1000000000): 0.545843449448699 44705
sin(1050): -0.4805001434937588 0229

In fact, I compiled this as a fat binary with code for the x86_64, i386 and ppc7400 architectures. And running each of these (natively or automatically in Rosetta) gave exactly the results above.

Intel CoreDuo, MacOS X.4 • 64bit AMD Athlon X2, 32bit Linux • Intel Pentium 4, 32bit Linux • Intel CoreDuo, Linux • Intel CoreDuo, Linux

This means we are running on 32bit Intel, using the same C tool as above or a perl script computing the same values.

sin(1): 0.8414709848078965 0488
sin(10): -0.5440211108893697 7445
sin(1000): 0.8268795405320025 2158
sin(10000): -0.3056143888882521 5310
sin(100000): 0.035748797972016 38287
sin(100000000): 0.931639027109 67927412
sin(1000000000): 0.54584344944 977825076
sin(1050): 0.70522152581331276622

PowerPC G4, Mac OS X.5 • Power3-II, Linux

The G4’s numbers are correct for the reasonable cases. They do differ from the others for the insane case, though.

sin(1): 0.8414709848078965 0488
sin(10): -0.5440211108893697 7445
sin(100): -0.50636564110975879 061
sin(1000): 0.8268795405320025 2158
sin(10000): -0.3056143888882521 5310
sin(100000): 0.03574879797201650 777
sin(100000000): 0.9316390271097260 1451
sin(1000000000): 0.545843449448699 55807
sin(1050): -0.63803097310803336129

Java on Intel Core2Duo, Mac OS X.5 • Java on Intel CoreDuo, Linux • AMD Dual Core Opteron, Linux

Java documentation claims that floating point numbers are formatted to display all relevant digits. I.e. That the number format is chosen in a way that the digits you see fully determine the number in question. This spares us the effort of the superfluous/wrong digits we see in the other cases.

sin(1): 0.8414709848078965
sin(10): -0.5440211108893698
sin(100): -0.5063656411097588
sin(1000): 0.8268795405320025
sin(10000): -0.30561438888825215
sin(100000): 0.03574879797201651
sin(100000000): 0.931639027109726
sin(1000000000): 0.5458434494486996
sin(1050): -0.4805001434937588

These results are pretty exact throughout. Differences in the last digit should be attributable to the inherent imprecision of the notation.



for i = [0:5 8 9 50]
 disp(['sin(' num2str(10^i) ') = ' sprintf('%.30g', sin(10^i))])
in version 7.5 on various platforms (64bit AMD with 32bit Linux, 64bit AMD with 64bit Linux 32bit Intel, Intel CoreDuo with MacOS X.4, PowerPC G4 with MacOS X.5) gave exactly the same results everywhere. These are correct in all meaningful digits even for the unreasonable case. In the reasonable cases, numbers are printed as they are on the G4.

sin(1): 0.8414709848078965 0487
sin(10): -0.5440211108893697 7445
sin(100): -0.50636564110975879 0605
sin(1000): 0.8268795405320025 215
sin(10000): -0.3056143888882521 5309
sin(100000): 0.03574879797201650 7773
sin(100000000): 0.9316390271097260 1450
sin(1000000000): 0.545843449448699 5580
sin(1050): -0.4805001434937588 0229

Sun sparcv9 • Itanium 2, Linux • UltraSPARC III, Solaris 8

Correct with numbers for the resonable cases printes as they are on the G4 Mac, except for the 1050 case which matches Intel Core2Duo.

sin(1): 0.8414709848078965 0488
sin(10): -0.5440211108893697 7445
sin(100): -0.50636564110975879 061
sin(1000): 0.8268795405320025 2158
sin(10000): -0.3056143888882521 5310
sin(100000): 0.03574879797201650 777
sin(100000000): 0.9316390271097260 1451
sin(1000000000): 0.545843449448699 55807
sin(1050): -0.4805001434937588 0229

Intel Core2Duo • PowerPC G4, Apple Calculator application, Mac OS X

The Apple Calculator application seems to add some smarts to the standard arithmetic library. It always returns 0 for sine once values reach 1016. The calculator was set to display 16 digits, the maximum it can do in Scientific mode. It seems to have a bug on Intel which causes it to display all sine values as positive numbers, though.

sin(1): 0.841470984807897
sin(10): (-)0.54402111088937
sin(100): (-)0.506365641109759
sin(1000): 0.826879540532003
sin(10000): (-)0.305614388888252
sin(100000): 0.03574879797202
sin(100000000): 0.931639027109726
sin(1000000000): 0.545843449448699 / 0.5458434494487
sin(1050): 0

bc running on Intel Core2Duo MacOS X.5, G4 MacOS X.5, Intel Linux

The bc command line utility is made as an arbitrary precision calculator. So I really assumed its results would be correct. They look pretty good at first. But at the higher numbers they are off a little. Run the tool using bc -l and execute the command

{s(1); s(10); s(100); s(1000); s(10000); s(100000); s(100000000); s(1000000000); s(10^50); s(100000000000000007629769841091887003294964970946560)}
once it is running.

sin(1): 0.84147098480789650665
sin(10): -0.54402111088936981340
sin(100): -0.50636564110975879365
sin(1000): 0.82687954053200256025
sin(10000): -0.30561438888825214137
sin(100000): 0.035748797972016509 20
sin(100000000): 0.9316390271097259 6889
sin(1000000000): 0.5458434494487 0046659
sin(1050): 0.84147098480789650665 / -0.63862926287572439913
sin(100000000000000007629769841091887003294964970946560): -0.22128023731494514406

In the 1050 case Core2Duo Mac OS X.5 gives the first result and the G4 Mac OS X.5 as well as the Linux system give the second result.

Arbitrary, sure – precision, my ass, I say. Kind of serves them right. After all bc is a typical bastion of user hostility. What would you deduce about the spirit in which it was designed when you keep in mind that you need to use the -l option for it to actually load its maths library, that to compute a sine, you need to use a function called s() and that they output numbers between 0 and 1 without a leading 0. Nicely frames a mindset for me…

Casio fx-85SA • Casio fx100D

A few tests were conducted with a Casio fx-85SA and fx-100D 1990s pocket calculators. With a 10-digit display their precision is even more limited than that of modern CPUs. Tehy also need a discernibly large fraction of a second to compute a sine and will simply give an error message when trying to compute the sine of a large number. It beats all of the other devices in imprecision:

sin(1): 0.841470984
sin(10): -0.54402111
sin(100): -0.506365641
sin(1000): 0.8268795 39
sin(10000): -0.3056143 66
sin(100000): 0.035748 381(5296)
sin(100000000): 0.931 747567

Casio pocket calculator showing the result for sin(100000000)


Version 7.3 on MicroVAX 3100 model 80. This one gives correct values as well but it wouldn’t compile the 1050 cases due to floating point overflows.

sin(1): 0.8414709848078965 0488
sin(10): -0.5440211108893698 1609
sin(100): -0.50636564110975879 061
sin(1000): 0.8268795405320025 6321
sin(10000): -0.3056143888882521 3922
sin(100000): 0.03574879797201650 951
sin(100000000): 0.9316390271097260 1451
sin(1000000000): 0.545843449448699 55807

OpenVMS Alpha

Versions 7.3-1 and 8.3 running on AlphaServer DS10L and DS20 respectively give correct results as well. Their floating point output puts zeros in the irrelevant places.

sin(1): 0.8414709848078965 0000
sin(10): -0.54402111088936977 000
sin(100): -0.50636564110975879 000
sin(1000): 0.82687954053200252 000
sin(10000): -0.30561438888825215 000
sin(100000): 0.03574879797201651 000
sin(100000000): 0.93163902710972601 000
sin(1000000000): 0.54584344944869956 000
sin(100000000000000010000000000000000000000000000000000): -0.4805001434937588 0000

PA-RISC 8500, HP-UX 11.0

This one is very similar to the Open VMS Alpha one above. It just seems to print floating point number s slightly differently by writing the digits 08 instead of 10 in the 100000 and 1050 cases:

sin(1): 0.8414709848078965 000
sin(10): -0.54402111088936977 000
sin(100): -0.50636564110975879 000
sin(1000): 0.82687954053200252 000
sin(10000): -0.30561438888825215 000
sin(100000): 0.035748797972016508 00
sin(100000000): 0.93163902710972601 000
sin(1000000000): 0.54584344944869956 000
sin(100000000000000008000000000000000000000000000000000): 0

MIPS R14000, IRIX64 6.5

Yet a slightly different way of writing out the numbers, I think. Interestingly, this system manages to have an extra decimal digit of precision compared to the others in most cases.

sin(1): 0.84147098480789650 000
sin(10): -0.54402111088936989 000
sin(100): -0.50636564110975879 000
sin(1000): 0.82687954053200252 000
sin(10000): -0.30561438888825215 000
sin(100000): 0.035748797972016508 00
sin(100000000): 0.93163902710972601 000
sin(1000000000): 0.545843449448699 56000
sin(100000000000000010000000000000000000000000000000000.00) = -0.48050014349375880 000

Google Search Calculator

Just ‘because we can’. Google only gives nine digits after the comma, so this may be a bit pointless. At least they came up with their own value for the 1050 case.

sin(1): 0.841470985
sin(10): -0.544021111
sin(100): -0.506365641
sin(1000): 0.826879541
sin(10000): -0.305614389
sin(100000): 0.035748798
sin(100000000): 0.931639027
sin(1000000000): 0.545843449
sin(1050): 0.842101214


While this investigation didn’t give the greatest results in terms of finding out whether or not Taylor series are used in real-world computations of standard trigonometric functions, they highlighted how implementation details can affect results.

The results for the 1050 case also highlight that depending on your environment you can get results which are off by more than 1. Keeping in mind, however, that entering 1050 into the fiendishly imprecise world of floating point computations means that the machine will try to compute the value of sin(100000000000000007629769841091887003294964970946560) instead, stresses once more that – at that extreme level at least – the main issue would be keeping a good idea about the limitations of the data types you are using.

And as double numbers are even less precise than integers above the 1016 range, it’s pretty absurd to even try using a sine function with such input. The results we get there, may help spotting differences between implementations, though. It might be preferable to always return 0 above some limit. I would say, though, that the documentation pointing you right to these problems could make things easier.

With this in mind, it’s clear that a far smaller number than 1050 would have given similar results, but the initial program which gave all these results used 1050, so here we are.

Some of the results seem strange to me. But how should they be interpreted? Who can identify the algorithm used?

Source Code

Most of the computations whose results are listed above were created with a small C program. It can be compiled using the gcc -lm command:

#include <stdio.h>
#include <math.h>

void mysin (double number) {
    printf("sin(%.2f): %.20f\n", number, sin(number));

int main (int argc, const char * argv[]) {
    // insert code here...
    return 0;

This code wrapped in an XCode 3 project as well as similar code for Java in a similar wrapping is available here for your amusement.

Going further

Only a few common platforms have been covered in this comparison. If you have access to other platforms, please grab the code and add the results in comments or send them by e-mail. It will be interesting to see whether more differences will appear.

I’d quite fancy trying to see which results you get from running sin(x) in an image kernel on Mac OS X. Is sin(x) computed in the processor or in the graphics chip? And in the latter case: Are there significant differences between the algorithms used? While it’s trivial to set up Quartz Composer to do the computation, I don’t know how to extract its result. Any hints on that are welcome.


Thanks to Steffen (most of the computations), Jakob (Linux on Intel CoreDuo), Leif (Java), Daniel (the Casio case), Stephen (Open VMS) and Dave (Itanium, Power, PA-RISC, UltraSPARC, MIPS) for doing computations on their machines and sharing them.

January 20, 2008, 15:12

Tagged as software.


Comment by Douglas Stetner: User icon

Don’t disparage my beloved bc! Try setting the scale in bc first.

bc -l
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
{s(1); s(10); s(100); s(1000); s(10000); s(100000); s(100000000); s(1000000000); s(10^50); s(100000000000000007629769841091887003294964970946560)}

To see a better comparison, here are the Mathimatica results and the bc results intermingled and sorted (makes for a good visual diff!). The ‘bc resuls are the longer ones!:


Cheers, Doug

January 21, 2008, 11:16

Comment by ssp: User icon

Doug: I don’t have Mathematica at hand right now, but the question is whether the last digits of bc’s result will be correct in the 100 digit result you give. And let’s say I have an assumption about what the answer for that question will be.

I think a calculator which claims to be precise should not print decimal digits in its results which might be wrong. Call me conservative, but I don’t think that this is particularly good output for a program:

echo "scale=2; s(1000);" | bc -l

The result should be 0.83 when printed with two digits.

[I took the liberty to insert some markup in your comment to make it display the way you intended.]

January 21, 2008, 11:35

Comment by Douglas Stetner: User icon

A poor workman blames his tools!

The man page for bc states:

There are two attributes of numbers, the length and the scale. The length is the total number of significant decimal digits in a number and the scale is the total number of decimal digits after the decimal point. For example: .000001 has a length of 6 and scale of 6. 1935.000 has a length of 7 and a scale of 3.


scale defines how some operations use digits after the decimal point.


If bc is invoked with the -l option, a math library is preloaded and the default scale is set to 20.

And last but not least:

(This version truncates results from divide and multiply operations.)


So basically, you have told bc to only worry about working with 2 decimal places, but you interpret that to mean be accurate to 2 decimal places. Two very different things.

If you say: scale = 2 1/7 + 1/7 + 1/7 + 1/7 + 1/7 + 1/7 + 1/7 .98

because 1/7 .14

and .14 * 7 = .98

So, if you want rounding, you need to do it yourself.

January 22, 2008, 8:29

Comment by ssp: User icon

Doug: Sure, I am aware of that. I did manage to get bc to compute sines after all, didn’t I? Heck, the beauty of open source means I could just read through the source code to learn what’s going on and how things are done, right? That’s exactly the user hostility of (most, not all) open source software which I wanted to address. I also don’t think I told the tool to do anything. Call me naïve, but I just launched it and assumed it would have reasonable presets. After a bit of cursing I found out how to make it compute sines and still assumed it would have reasonable presets.

I think the problem here is that there are two different kinds of precision. One is the display precision and the other is the precision used in computations. As you will be aware of, you may need to begin a computation with more digits of precision than you want as output to avoid rounding errors or other imprecisions from running into the results.

My idea of good software would be that the software either automatically increases the number of digits of precision used for computation so all the digits you request are correct or it omits the digits in the result which it doesn’t know to be correct. Instead, bc prefers to add everything to its output. Even the last digit of the example for π in the man page (echo “scale=10; 4*a(1)” | bc -l is plainly wrong.

While a pragmatist would just add a few extra digits to the ‘scale’ to be on the safe side, that’s not a particularly good solution. Once you do several computations in a row, how are you going to know how many of the digits you need to drop and how many of them will represent a correct result? That seems far from clear to me. Particularly for more involved computations, the error-proneness of which you cannot easily judge.

How many decimal digits of the sine will be correct for which x? at which scale? If x is slightly large, it won’t be that many.

scale = 20
a = s(10^15)
scale = 30
b = s(10^15)

January 22, 2008, 11:36

Comment by Douglas Stetner: User icon

Ahhh, but this started with the discussion of precision, not user friendliness. The precision of bc was called into question and I still disagree with that.

I am the first to admit a lot of unix/open source tools are not what people would call user friendly, but they were mainly written for and are useful to unix geeks, and, like bc, they fill that niche very well.

Cheers, Doug

January 22, 2008, 15:14

Comment by ssp: User icon

Hehe, my impression was that my only ‘attack’ on bc was calling it user-hostile.

That said, I didn’t even want to discuss the precision issue further, because the tool does give incorrect results. sin(x) is well-defined. And how a tool computes it internally shouldn’t matter.

In fact I don’t see a straightforward way to make bc give me a result which is guaranteed to be correct in all digits. Which scale will you choose? How many digits will you chop off? To which extent will this depend on the function that will be computed? And on the parameter passed to that function?

I’m not a Unixy person, but my impression of the Unix world is that a tool would be considered ‘good’ if it fits neatly in one of those pipes. I don’t really see how bc can (easily, without needing another computation to determine how many digits of the bc computation are correct) fit in a fictional pipe like

createmathexpression | bc | processresult

as it is hard to know how much of the output of bc is correct.

As for our sine example: I still don’t see how this explains anything. Perhaps my man-page reading skills aren’t the best. But to me

The math functions will calculate their results to the scale set at the time of their call.

sounds like the s() function should give a result that is precise to the full length of that ‘scale’, i.e. 20 in my example. In particular, it does not sound like they’ll start with precision 20 variables, and use them in each step of whichever undisclosed (hopefully Taylor series :) computations they do – accumulating errors on the way.

January 22, 2008, 17:38

Comment by ssp: User icon

For additional amusement let’s try to accumulate the computational errors to make everything wrong. I really think that shouldn’t happen and it’ll be non-trivial to work out correctly which scale we need to get a certain number of correct digits.

bc -l
bc 1.06 […]
s(s(10^15) * 10^15) 
scale = 25
s(s(10^15) * 10^15)
scale = 30
s(s(10^15) * 10^15)
scale = 40
s(s(10^15) * 10^15)

January 22, 2008, 17:55

Comment by Douglas Stetner: User icon

It is open source:

/* Sin(x)  uses the standard series:
   sin(x) = x - x^3/3! + x^5/5! - x^7/7! ... */

define s(x) {
  auto  e, i, m, n, s, v, z

  /* precondition x. */
  z = scale 
  scale = 1.1*z + 2;
  v = a(1)
  if (x 

But in any case, lots of people like to say that they should not have to know how things work to use them, but in reality they do need to know, and there are a million everyday things that we must know how they work to use….

Why doesn’t my food, in a metal pot and lid, cook in my microwave oven? Hmm, I need to know how the oven works.

Why does my car not work? Hmm, I need petrol for the internal combustion engine, alcohol doesn’t work.

I like to work in base 16. How come this calculator says 9+2 is 11 when it is obviously B? I need to know how the calculator works.

etc. etc.

The precision is there, but you do need to understand how to use bc.

Cheers, Doug

January 23, 2008, 11:40

Comment by ssp: User icon

Sorry, I remain unconvinced. Just because I can read the source, doesn’t mean I should need to read it just to do a simple computation. I could have done the computation manually if I wanted to go through that effort.

As for the microwave: It comes with a sticker or instruction manual saying I shouldn’t put metal in there. As long as I stick to that, things will work just fine. I only need to know about the workings of the microwave if I want to use it outside those limits.

Same for the car. I don’t need to understand how it works. (And what would ‘understanding’ mean anyway? Understand there is some combustion? Know how it drives the mechanics? Being able to write down the chemical reactions? Being able to compute the energy provided by them? Being able to work out the physics of those molecular reactions?) I was told I need to refuel it. And it even comes with a label telling me which fuel it needs and a fuel gauge. the rest isn’t particularly relevant for using it.

For the hexadecimal, I’d say it’s an agreed standard to work in decimal. If you want to deviate from that standard, you’ll have to find out how things work.

And by the same argument I’d say that when doing numerical computations I’d expect them to be correct to all displayed digits. (Or, that at least the potential error is explicitly given.)

bc does neither of that. It gives me results and leaves it to me to figure out how many of the digits are correct (see the example in my previous comment). That doesn’t exactly inspire trust, does it? How will I be able to use this as a helpful too in a Unix pipe (two comments ago)? I have no idea.

Perhaps it’s a bug in bc? As the problem manifests itself mainly for large numbers, I wonder whether it’s a good idea to not increase the scale for large numbers. Perhaps changing the scale computation in the s function to depend on x, say, by taking something like ln(x) instead of 1.1 in the formula for setting the scale at the beginning of the function, improves things. At least it gives considerably better results for me.

[Note: I haven’t really thought this through, this is just intuition and a little test. I won’t promise that this will always be correct. And I’m pretty sure it’s less than perfect in terms of efficiency as well.]

Could it be that the scale setting by bc’s implemenation of the Taylor Series is just sloppy? Or would you say that the wrong results bc gives are better than the correct ones it may be able to give as well? Would you also say that a calculator should have a built-in sine function if I’m required to implement my own one just to get correct results?

The ironic thing is: I got into this via Taylor Series. And, bcis one of the few instances where they are used. Yet, in practice, it gives among the worst results by default. Being even further off than 32-bit Intel.

January 23, 2008, 12:27

Comment by Douglas Stetner: User icon

Well I am sick of arguing 8-)

If you can read the microwave instructions, you should be able to read the bc man page and understand that ‘scale’ does not equal precision, and given that, as with any calculation, you cannot know to what precision the answer is unless you know what precision is used in all the intermediary calculations.

January 23, 2008, 14:15

Comment by ssp: User icon

As I quoted before:

The math functions will calculate their results to the scale set at the time of their call.

Call me naïve, but to me it reads like a promise that by default bc computes the sine to the precision given by the current stale. And, frankly, everything else wouldn’t make much sense.

Even if you could find some other note that ‘to the scale’ is supposed to mean something completely different, it would make more sense. Because at the end of the day even open source tools like bc probably aim to be useful in some way other than trying to be smarter than people who accidentally use them.

January 23, 2008, 19:08

Add your comment

« Taylor SeriesMainFerns and Fern Allies »

Comments on




This page

Out & About

pinboard Links


Received data seems to be invalid. The wanted file does probably not exist or the guys at last.fm changed something.