So I am working with some matlab code converting to c code by hand. I am just wondering if there is a c equivalent to the sind and cosd functions I'm seeing in the matlab code. I guess this returns the answer in degrees rather than the c sin and cos function that gives the result in radians. I guess I could just multiply the result by 180/pi but was just wondering if there was a library function in math.h I'm not seeing. Or even if there is something in the gsl library that does that.
-
Also related: have a look at the [MATLAB Coder](http://www.mathworks.com/products/matlab-coder/)... – Eitan T Mar 10 '13 at 16:45
5 Answers
H2CO3's solution will have catastrophic loss of precision for large arguments due to the imprecision of M_PI
. The general, safe version for any argument is:
#define sind(x) (sin(fmod((x),360) * M_PI / 180))

- 208,859
- 35
- 376
- 711
-
Isn't `M_PI` a `double` itself? (I'm not pulling hair, just asking - this is nice and safe.) – Mar 10 '13 at 16:45
-
1It wouldn't help if `M_PI` had 1000 bits of precision. 2**1000 times `M_PI` would still be completely wrong in the places that count modulo 2π. – R.. GitHub STOP HELPING ICE Mar 10 '13 at 16:47
-
1Suppose `x` is `0x168p1000`. `sind(x)` should be 0. But if you multiply `0x168p1000` times `M_PI`, the result will most definitely not be anywhere near an integer multiple of 360π. – R.. GitHub STOP HELPING ICE Mar 10 '13 at 16:49
-
Another small issue: if you care about floating point exception flags, `sind(0x168p1000)` should not raise inexact. – R.. GitHub STOP HELPING ICE Mar 10 '13 at 16:50
-
1@R..: I see, and I agree that this helps. But the values of `x` that are improved by this technique are somewhat sparsely-distributed, no? Indeed, if you have 0x168p1000, it's likely that you've already lost precision before you apply the `sin` function. – Oliver Charlesworth Mar 10 '13 at 16:56
-
All large values of `x` are affected by the loss of precision. Only integer multiples of 90 are affected by the inexact-exception issue, of course. – R.. GitHub STOP HELPING ICE Mar 10 '13 at 16:58
-
And there's no reason to believe a huge angle must necessarily be imprecise when it's given in degrees. For instance if your operation is to take an initial angle and keep doubling it (as in squaring a complex number), each successive iteration will be exact until you hit infinity. – R.. GitHub STOP HELPING ICE Mar 10 '13 at 17:00
-
@R..: I guess what I mean is, the value 0x168p1000 represents an interval of 0x1p947 (or thereabouts). In terms of error intervals (this may not be the correct term!), I'm not sure there's any significant difference between your approach and the "naive" approach. But yes, I guess it's possible that you really do want to represent `0x168p1000`, and not `(0x168p1000 + 1)`! – Oliver Charlesworth Mar 10 '13 at 17:02
-
1@OliCharlesworth: 0x168p1000 does not represent an interval of about 0x1p947. Per IEEE 754, it represent exactly one number, 168 times 2 to the power of 1000. People may then go on to add another layer of semantics to their code, using precise values to represent imprecise values, but a mathematical library routine cannot assume that in general. It should assume the input it is given stands for exactly what the IEEE 754 standard says. – Eric Postpischil Mar 10 '13 at 21:53
-
@EricPostpischil: I appreciate that. I'm just not sure it's fair to describe the "naive" approach as a "catastrophic loss of precision", when the alternative presented above (as correct as it is) fixes it for precisely one value in the example interval... – Oliver Charlesworth Mar 10 '13 at 22:11
-
@Oli: "Catastrophic loss of precision" is a technical term meaning (roughly) that the magnitude of the error approaches or exceeds the magnitude of the value in question, so that the result is meaningless. I'm not sure what interval you're talking about. The version of the function with `fmod` does not fix the result just for a few points and leave it broken for the rest; it fixes it for all inputs. – R.. GitHub STOP HELPING ICE Mar 10 '13 at 23:26
-
@R.. By "interval" I'm referring to the fact that (despite Eric's correct assertion that IEEE-754 is not based on interval arithmetic) *in practice*, it's unlikely that an input value of this magnitude has not already undergone significant rounding (especially if we're talking about an angle), and so is already "meaningless". And so in that sense, applying your fix (which I totally agree is correct) doesn't render the result any more meaningful that H2CO3's approach. – Oliver Charlesworth Mar 10 '13 at 23:31
-
1@OliCharlesworth: The choice between designing a routine to be accurate for those times when it is given an accurate input and designing a routine to be inaccurate because it is often given inaccurate input is clear. Good software avoids losing accuracy and does not assume it is okay to screw up. I am aware that many people use floating point in atrociously sloppy ways. They should not. Please stop advocating bad software practices. – Eric Postpischil Mar 11 '13 at 00:35
-
@EricPostpischil: Don't worry, I'm not disagreeing. I'm merely pointing out (in a very roundabout way, admittedly) that the cases where this improves things, *in practice*, are very limited. To the extent that, depending on requirements, one might make a conscious decision to forego this improvement, if the overhead imposed by `fmod` was non-negligible. – Oliver Charlesworth Mar 11 '13 at 00:38
-
I think there are two likely ways one could arrive at a huge angle value. One, repeatedly rotating by a small amount, will eventually lose precision and "get stuck". The other, repeatedly multiplying the angle (which corresponds to powering a point on the unit circle) will only lose precision if the factor is not a power of two. Since in the other cases you already have a bug by working with large angles, I'm going to assume you're in the case where the code is correct and is repeatedly scaling an angle by a power of two. – R.. GitHub STOP HELPING ICE Mar 11 '13 at 01:02
-
I do agree that there are plenty of cases where you don't want the overhead of `fmod`: whenever the angle is kept small. I would also say that best practices when writing code that works with angles is to argument-reduce before each step that's non-exact so as not to waste bits of precision on whole-periods. But in order to match the matlab function, and in order to be a correct `sind` on the full domain, the argument reduction needs to happen as part of `sind`. – R.. GitHub STOP HELPING ICE Mar 11 '13 at 01:07
-
It would be interesting to see what Matlab gives for `sind(0x168p1000)`... Has anyone tried? – Floris Mar 24 '13 at 17:51
-
@R.. is there a similar problem with cosd()? Presumably `#define cosd(x) (cos((x) * M_PI / 180)` should be `#define cosd(x) (cos(fmod((x),360) * M_PI / 180)`?? Thanks! – thclark Feb 03 '17 at 17:58
-
No, the trigonometric functions in the C standard library all work in radians. But you can easily get away with a macro or an inline function:
#define sind(x) (sin((x) * M_PI / 180))
or
inline double sind(double x)
{
return sin(x * M_PI / 180);
}
Note that the opposite of the conversion is needed when you want to alter the return value of the function (the so-called inverse or "arcus" functions):
inline double asind(double x)
{
return asin(x) / M_PI * 180;
}
No, they are not available in C library. You will only find radian value arguments or return values in C library.

- 5,063
- 1
- 17
- 21
Also, note that sind and cosd etc do not return a result in degrees, they take their arguments in degrees. It is asind and acosd that return their results in degrees.

- 875
- 6
- 12
Not in the C library. All trigonometric C library functions take arguments as radian values not degree. As you said you need to perform the conversion yourself or use a dedicated library.

- 142,963
- 15
- 272
- 331