Skip to content

Commit

Permalink
Merge pull request #61 from georust/mkirk/sincosd-perf
Browse files Browse the repository at this point in the history
use libm remquo and copysign for a little speedup
  • Loading branch information
michaelkirk committed Feb 6, 2024
2 parents 4891fc4 + a2c65f4 commit 42ced39
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 56 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ default = ["accurate"]

[dependencies]
accurate = { version = "0.3", optional = true, default-features = false }
libm = { version = "0.2.8", default-features = false }

[dev-dependencies]
approx = "0.5.1"
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,16 @@ Which produces output like:

```text
direct (c wrapper)/default
time: [24.055 µs 24.085 µs 24.117 µs]
time: [24.046 µs 24.071 µs 24.099 µs]
direct (rust impl)/default
time: [27.760 µs 27.810 µs 27.867 µs]
time: [26.129 µs 26.168 µs 26.211 µs]
inverse (c wrapper)/default
time: [46.461 µs 47.435 µs 48.557 µs]
time: [45.061 µs 45.141 µs 45.227 µs]
inverse (rust impl)/default
time: [70.488 µs 70.841 µs 71.356 µs]
time: [67.739 µs 67.796 µs 67.865 µs]
```

Showing that, at least in this benchmark, the Rust implementation is 16-52% slower than the c bindings.
Showing that, at least in this benchmark, the Rust implementation is 10-50% slower than the c bindings.
121 changes: 70 additions & 51 deletions src/geomath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,66 +136,30 @@ pub fn ang_diff(x: f64, y: f64) -> (f64, f64) {
}
}

pub fn fmod(x: f64, y: f64) -> f64 {
x % y
}

/// Compute sine and cosine of x in degrees
pub fn sincosd(x: f64) -> (f64, f64) {
// r = math.fmod(x, 360) if Math.isfinite(x) else Math.nan
let mut r = if x.is_finite() {
fmod(x, 360.0)
} else {
std::f64::NAN
};
let (mut r, q) = libm::remquo(x, 90.0);

// q = 0 if Math.isnan(r) else int(round(r / 90))
let mut q = if r.is_nan() {
0
} else {
(r / 90.0).round() as i32
};

// r -= 90 * q; r = math.radians(r)
r -= 90.0 * q as f64;
r = r.to_radians();

// s = math.sin(r); c = math.cos(r)
let s = r.sin();
let c = r.cos();

// q = q % 4
q %= 4;

// if q == 1:
// s, c = c, -s
// elif q == 2:
// s, c = -s, -c
// elif q == 3:
// s, c = -c, s

let q = if q < 0 { q + 4 } else { q };
let (mut sinx, mut cosx) = r.sin_cos();

let (s, c) = if q == 1 {
(c, -s)
} else if q == 2 {
(-s, -c)
} else if q == 3 {
(-c, s)
} else {
debug_assert_eq!(q, 0);
(s, c)
(sinx, cosx) = match q as u32 & 3 {
0 => (sinx, cosx),
1 => (cosx, -sinx),
2 => (-sinx, -cosx),
3 => (-cosx, sinx),
_ => unreachable!(),
};

// # Remove the minus sign on -0.0 except for sin(-0.0).
// # On Windows 32-bit with python 2.7, math.fmod(-0.0, 360) = +0.0
// # (x, c) here fixes this bug. See also Math::sincosd in the C++ library.
// # AngNormalize has a similar fix.
// s, c = (x, c) if x == 0 else (0.0+s, 0.0+c)
// return s, c
let (s, c) = if x == 0.0 { (x, c) } else { (0.0 + s, 0.0 + c) };
// special values from F.10.1.12
cosx += 0.0;

(s, c)
// special values from F.10.1.13
if sinx == 0.0 {
sinx = sinx.copysign(x);
}
(sinx, cosx)
}

// Compute atan2(y, x) with result in degrees
Expand Down Expand Up @@ -532,4 +496,59 @@ mod tests {
4.124513511893872e-05
);
}

// corresponding to tests/signtest.cpp
mod sign_test {
use super::*;
fn is_equiv(x: f64, y: f64) -> bool {
(x.is_nan() && y.is_nan()) || (x == y && x.is_sign_positive() == y.is_sign_positive())
}

macro_rules! check_sincosd {
($x: expr, $expected_sin: expr, $expected_cos: expr) => {
let (sinx, cosx) = sincosd($x);
assert!(
is_equiv(sinx, $expected_sin),
"sinx({}) = {}, but got {}",
$x,
$expected_sin,
sinx
);
assert!(
is_equiv(cosx, $expected_cos),
"cosx({}) = {}, but got {}",
$x,
$expected_cos,
cosx
);
};
}

#[test]
fn sin_cosd() {
check_sincosd!(f64::NEG_INFINITY, f64::NAN, f64::NAN);
check_sincosd!(-810.0, -1.0, 0.0);
check_sincosd!(-720.0, -0.0, 1.0);
check_sincosd!(-630.0, 1.0, 0.0);
check_sincosd!(-540.0, -0.0, -1.0);
check_sincosd!(-450.0, -1.0, 0.0);
check_sincosd!(-360.0, -0.0, 1.0);
check_sincosd!(-270.0, 1.0, 0.0);
check_sincosd!(-180.0, -0.0, -1.0);
check_sincosd!(-90.0, -1.0, 0.0);
check_sincosd!(-0.0, -0.0, 1.0);
check_sincosd!(0.0, 0.0, 1.0);
check_sincosd!(90.0, 1.0, 0.0);
check_sincosd!(180.0, 0.0, -1.0);
check_sincosd!(270.0, -1.0, 0.0);
check_sincosd!(360.0, 0.0, 1.0);
check_sincosd!(450.0, 1.0, 0.0);
check_sincosd!(540.0, 0.0, -1.0);
check_sincosd!(630.0, -1.0, 0.0);
check_sincosd!(720.0, 0.0, 1.0);
check_sincosd!(810.0, 1.0, 0.0);
check_sincosd!(f64::INFINITY, f64::NAN, f64::NAN);
check_sincosd!(f64::NAN, f64::NAN, f64::NAN);
}
}
}

0 comments on commit 42ced39

Please sign in to comment.