# How does C compute sin() and other math functions?

I've been poring through .NET disassemblies and the GCC source code, but can't seem to find anywhere the actual implementation of `sin()` and other math functions... they always seem to be referencing something else.

Can anyone help me find them? I feel like it's unlikely that ALL hardware that C will run on supports trig functions in hardware, so there must be a software algorithm somewhere, right?

I'm aware of several ways that functions can be calculated, and have written my own routines to compute functions using taylor series for fun. I'm curious about how real, production languages do it, since all of my implementations are always several orders of magnitude slower, even though I think my algorithms are pretty clever (obviously they're not).

27

In GNU libm, the implementation of `sin` is system-dependent. Therefore you can find the implementation, for each platform, somewhere in the appropriate subdirectory of sysdeps.

One directory includes an implementation in C, contributed by IBM. Since October 2011, this is the code that actually runs when you call `sin()` on a typical x86-64 Linux system. It is apparently faster than the `fsin` assembly instruction. Source code: sysdeps/ieee754/dbl-64/s_sin.c, look for `__sin (double x)`.

This code is very complex. No one software algorithm is as fast as possible and also accurate over the whole range of x values, so the library implements several different algorithms, and its first job is to look at x and decide which algorithm to use.

• When x is very very close to 0, `sin(x) == x` is the right answer.

• A bit further out, `sin(x)` uses the familiar Taylor series. However, this is only accurate near 0, so...

• When the angle is more than about 7°, a different algorithm is used, computing Taylor-series approximations for both sin(x) and cos(x), then using values from a precomputed table to refine the approximation.

• When |x| > 2, none of the above algorithms would work, so the code starts by computing some value closer to 0 that can be fed to `sin` or `cos` instead.

• There's yet another branch to deal with x being a NaN or infinity.

This code uses some numerical hacks I've never seen before, though for all I know they might be well-known among floating-point experts. Sometimes a few lines of code would take several paragraphs to explain. For example, these two lines

``````double t = (x * hpinv + toint);
double xn = t - toint;
``````

are used (sometimes) in reducing x to a value close to 0 that differs from x by a multiple of ?/2, specifically `xn` × ?/2. The way this is done without division or branching is rather clever. But there's no comment at all!

Older 32-bit versions of GCC/glibc used the `fsin` instruction, which is surprisingly inaccurate for some inputs. There's a fascinating blog post illustrating this with just 2 lines of code.

fdlibm's implementation of `sin` in pure C is much simpler than glibc's and is nicely commented. Source code: fdlibm/s_sin.c and fdlibm/k_sin.c

Tuesday, June 1, 2021

81

In the example you gave:

``````vector<int>::const_iterator it = myvector.begin();
``````

if `myvector` isn't const the non-const version of `begin()` will be called and you will be relying on an implicit conversion from iterator to const_iterator.

Monday, June 14, 2021

10

`std::unique_ptr` is the C++11 way to express exclusive ownership, but one of its most attractive features is that it easily and efficiently converts to a `std::shared_ptr`.

This is a key part of why `std::unique_ptr` is so well suited as a factory function return type. Factory functions can’t know whether callers will want to use exclusive ownership semantics for the object they return or whether shared ownership (i.e., `std::shared_ptr`) would be more appropriate. By returning a `std::unique_ptr`, factories provide callers with the most efficient smart pointer, but they don’t hinder callers from replacing it with its more flexible sibling.

`std::shared_ptr` to `std::unique_ptr` is not allowed. Once you’ve turned lifetime management of a resource over to a `std::shared_ptr`, there’s no changing your mind. Even if the reference count is one, you can’t reclaim ownership of the resource in order to, say, have a `std::unique_ptr` manage it.

Reference: Effective Modern C++. 42 SPECIFIC WAYS TO IMPROVE YOUR USE OF C++11 AND C++14. Scott Meyers.

In short, you can easily and efficiently convert a `std::unique_ptr` to `std::shared_ptr` but you cannot convert `std::shared_ptr` to `std::unique_ptr`.

For example:

``````std::unique_ptr<std::string> unique = std::make_unique<std::string>("test");
std::shared_ptr<std::string> shared = std::move(unique);
``````

or:

``````std::shared_ptr<std::string> shared = std::make_unique<std::string>("test");
``````
Sunday, June 27, 2021

91

`Math.cos()` expects the parameter to be in radians. This will return the result you need:

``````Math.cos(Math.toRadians(50));
``````
Saturday, July 3, 2021

32
``````>>> import math
>>> print math.sin.__doc__
sin(x)

Return the sine of x (measured in radians).
``````

math.sin expects its argument to be in radians, not degrees, so:

``````>>> import math