How to compute correctly rounded trigonometric functions in degrees?

前端 未结 4 1473
轻奢々
轻奢々 2021-01-02 18:32

How could I define trigonometric functions that take arguments in degrees instead of the usual radians, and compute correctly rounded results for these arguments?

Mu

相关标签:
4条回答
  • 2021-01-02 18:50

    The only rationals q for which cosdeg(360q) is rational have 1, 2, 3, 4, or 6 as the denominator. This paper by Joerg Jahnel contains a short and beautiful proof using field theory in section 6. (Indeed, the author characterises the degree of the algebraic number cosdeg(360q) using Euler's totient function.) So there is no floating-point q such that cosdeg(360q) is halfway between two adjacent floating-point numbers.

    So I guess the answer is "about the same way you implement sin and friends for radians," though @gnasher729 makes the excellent point that argument reduction for degrees is much, much nicer.

    0 讨论(0)
  • 2021-01-02 18:53

    Well, this is a difficult question. Let me clarify some points:

    • What precision is required for the the output? Is it IEEE 754 single or double precision or non standard? Moreover, I assume, that input, i.e. the one represented in degrees, should represented in the same precision as outputs, as this is the case for the normal radian inputs.
    • What is your performance metrics? CRlibm is optimized to produce correctly rounded double precision results. On the other side, MPFR is used for arbitrary precision but it is much slower than CRlibm when you need only double precision output.
    • What is your working range? i.e. [min argument, max argmunet]? This matters for CRlibm as it works for double precision ranges. However, it won't really matter for MPFR.

    I basically recommend to use MPFR if you are obliged to use inputs only in degrees. Let me remind you that any argument in degrees, when it is multiplied by (Pi/180), it produces a transcendental number. However, What is passed to the trigonometric function is the floating point representation rounded, preferably rounded to nearest integer, to the working precision.

    I recommend you to do as follows:

    1. Use MPFR, use the C library whenever possible as it offers much better performance than its bindings.
    2. Set the MPFR precision much higher than your target precision. For example (target precision + 300). By doing this, you avoid any loss of accuracy for the operation ((Argument*Pi)/180). This can be done easily in MPFR C library by mpfr_set_default_prec().
    3. Do the operation: X_n=(Argument*Pi)/180, and then execute Sin(X_n) or whatever function you want. There is a constant Pi in MPFR, which is represented within your working precision
    4. Round your result to the target precision.

    "Elementary functions", by Muller shows statistically that most, NOT ALL, of the hard cases are correctly rounded if the working precision is slightly larger than twice the target precision. But in your case, as the input is theoretically transcendental, to be safe, at the expense of the performance, make the working precision much higher than the target. Actually 10x is totally sufficient for almost 100% of cases, if you require up to double precision final result.

    If you need a low precision, i.e. single precision or lower, it is feasible to do exhaustive test to decide on the lowest working precision which produces all cases correctly rounded.

    0 讨论(0)
  • 2021-01-02 19:00

    It's difficult. On the positive side, you can reduce the argument to +/- 45 degrees exactly. So you need correctly rounded results between +/- 45 degrees. For very small x, sin (x) is about x * (pi / 180) which is hard enough to get rounded exactly.

    To get mostly correctly rounded results for the sine function for example, take -45 <= x <= 45. Split x into xhi = round (512 x) / 512 and xlo = x - xhi. Let sin (x degrees) ≈ ax - bx^3. Round a and b so that s (x) a*xhi - b * (xhi^3) is calculated exactly. Calculate the remainder sin (x degrees) - s (x) carefully; the rounding error should be quite small because the result is small. Add to s (x), this will most of the time give the correctly rounded result.

    0 讨论(0)
  • 2021-01-02 19:09

    You first need to detect the exact cases, and this has already been answered. Now, for the other cases, there's the well-known problem of the table maker's dilemma. If your arithmetic has a fixed (and small) precision and you want a certified bound on the intermediate precision that might be needed, there are two known solutions:

    • Obtain a bound based on Nesterenko and Waldschmidt's theorem, as described in Section 4.3 of my PhD thesis (BTW, I think this would also give you the form of the exact cases). But you will get very large bounds on the precision (at least several millions bits?).
    • Find the hardest-to-round case. It suffices to do the search in [0,180], since any larger argument will reduce to a value in [0,180] with the same fractional part (since the period is an integer).
    0 讨论(0)
提交回复
热议问题