1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use ;
use taylor_series;
/// Reduces `value` (in radians) to a remainder in `[-pi/4, pi/4]` plus a quadrant `0..4`,
/// by subtracting the nearest multiple of `pi/2`.
///
/// [`sin`]/[`cos`] converge to full precision only for arguments near zero, since their
/// underlying [`taylor_series`] runs a fixed number of terms; reducing to the nearest
/// quadrant (rather than only to `[-pi, pi]`) keeps that fixed budget accurate across the
/// whole documented `[-2*pi, 2*pi]` domain, including near a zero crossing of `sin`/`cos`
/// itself — reducing only to `[-pi, pi]` can leave the remainder close to `pi`, where the
/// series needs far more terms than it does near zero for the same precision.
///
/// `quadrant` tells the caller which of `sin(r)`/`cos(r)`/`-sin(r)`/`-cos(r)` reconstructs
/// `sin(value)` (and the complementary choice for `cos(value)`).
///
/// The multiple is found via a truncating cast to `i64` (rounded to nearest by biasing the
/// quotient by +-0.5 first) rather than a `libm` `floor`/`round` call, so this stays
/// available in `no_std` builds without a `libm` dependency.
///
/// `pi/2` itself is irrational and only approximated by `PIO2_HI`, so subtracting it in one
/// step would bake that single-`f64`-ulp approximation error into the remainder — small in
/// absolute terms, but large relative to the remainder near a zero crossing of `sin`/`cos`
/// (where the remainder itself is close to zero). Splitting `pi/2` into a leading `PIO2_HI`
/// (exactly representable, so `value - k*PIO2_HI` loses no bits beyond ordinary
/// cancellation) and a trailing correction `PIO2_LO` — a Cody-Waite reduction — recovers
/// most of that precision.
pub
/// `f32` counterpart of [`reduce_f64`].
///
/// The subtraction that isolates the remainder is done in `f64`, not `f32`: `f32`'s ~7
/// decimal digits of precision aren't enough to represent `PIO2_HI`/`PIO2_LO` (and thus the
/// remainder near a zero crossing) as tightly as `f64` can, so reducing in `f32` arithmetic
/// directly reintroduces the same cancellation error `f64`'s reduction avoids. Widening to
/// `f64` for this one step, then narrowing the (already small) remainder back down, costs
/// nothing at runtime worth avoiding in exchange for using the wider type's precision where
/// it matters most.
pub
/// Computes the sine of `value` (in radians) via [`taylor_series`]:
/// `sin(x) = x - x^3/3! + x^5/5! - ...`.
///
/// `zero` and `one` are passed in by the caller, since this is generic over any type with
/// the right arithmetic operations rather than over [`super::Scalar`] itself (`Scalar::sin`
/// is implemented in terms of this function, so the function itself can't depend on
/// `Scalar`), the same reason [`super::newton_raphson::newton_raphson`] takes `zero`/`two`
/// explicitly.
pub
/// Computes the cosine of `value` (in radians) via [`taylor_series`]:
/// `cos(x) = 1 - x^2/2! + x^4/4! - ...`.
///
/// Same `zero`/`one` parameter convention as [`sin`], for the same reason.
pub