Skip to content

Commit 99be102

Browse files
authored
Rollup merge of #72486 - Ralith:asinh-fix, r=dtolnay
Fix asinh of negative values Rust's current implementation of asinh has [large errors](https://www.wolframalpha.com/input/?i=arcsinh%28x%29%2C+ln%28x%2B%28x%5E2%2B1%29%5E0.5%29%2C+x+from+-67452095.07139316+to+0) in its negative range. ~These are (mostly) not numerical, but rather seem due to an incorrect implementation.~ This appears to be due to avoidable catastrophic cancellation. [Playground before/after](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=bd04ae6d86d06612e4e389a8b95d19ab). [glibc uses](https://github.com/bminor/glibc/blob/81dca813cc35f91414731fdd0ff6b756d5e1827f/sysdeps/ieee754/dbl-64/s_asinh.c#L56) abs here. Many thanks to @danieldeankon for finding this weird behavior, @jebrosen for diagnosing it, and @toasteater for identifying the probable implementation error!
2 parents 9c54c65 + 35a2915 commit 99be102

File tree

2 files changed

+6
-10
lines changed

2 files changed

+6
-10
lines changed

src/libstd/f32.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -832,11 +832,7 @@ impl f32 {
832832
#[stable(feature = "rust1", since = "1.0.0")]
833833
#[inline]
834834
pub fn asinh(self) -> f32 {
835-
if self == Self::NEG_INFINITY {
836-
Self::NEG_INFINITY
837-
} else {
838-
(self + ((self * self) + 1.0).sqrt()).ln().copysign(self)
839-
}
835+
(self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self)
840836
}
841837

842838
/// Inverse hyperbolic cosine function.
@@ -1413,6 +1409,8 @@ mod tests {
14131409
assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271
14141410
assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32);
14151411
assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32);
1412+
// regression test for the catastrophic cancellation fixed in 72486
1413+
assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32);
14161414
}
14171415

14181416
#[test]

src/libstd/f64.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -834,11 +834,7 @@ impl f64 {
834834
#[stable(feature = "rust1", since = "1.0.0")]
835835
#[inline]
836836
pub fn asinh(self) -> f64 {
837-
if self == Self::NEG_INFINITY {
838-
Self::NEG_INFINITY
839-
} else {
840-
(self + ((self * self) + 1.0).sqrt()).ln().copysign(self)
841-
}
837+
(self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self)
842838
}
843839

844840
/// Inverse hyperbolic cosine function.
@@ -1442,6 +1438,8 @@ mod tests {
14421438
// issue 63271
14431439
assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64);
14441440
assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64);
1441+
// regression test for the catastrophic cancellation fixed in 72486
1442+
assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083);
14451443
}
14461444

14471445
#[test]

0 commit comments

Comments
 (0)