Skip to content

Commit 45bfb1c

Browse files
authored
Rollup merge of #104553 - mwillsey:asinh-acosh-accuracy, r=thomcc
Improve accuracy of asinh and acosh This PR addresses the inaccuracy of `asinh` and `acosh` identified by the [Herbie](http://herbie.uwplse.org/) tool, `@pavpanchekha,` `@finnbear` in #104548. It also adds a couple tests that failed in the existing implementations and now pass. Closes #104548 r? rust-lang/libs
2 parents 5caac92 + 26b23f4 commit 45bfb1c

File tree

4 files changed

+32
-4
lines changed

4 files changed

+32
-4
lines changed

library/std/src/f32.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -880,7 +880,9 @@ impl f32 {
880880
#[stable(feature = "rust1", since = "1.0.0")]
881881
#[inline]
882882
pub fn asinh(self) -> f32 {
883-
(self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self)
883+
let ax = self.abs();
884+
let ix = 1.0 / ax;
885+
(ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self)
884886
}
885887

886888
/// Inverse hyperbolic cosine function.
@@ -900,7 +902,11 @@ impl f32 {
900902
#[stable(feature = "rust1", since = "1.0.0")]
901903
#[inline]
902904
pub fn acosh(self) -> f32 {
903-
if self < 1.0 { Self::NAN } else { (self + ((self * self) - 1.0).sqrt()).ln() }
905+
if self < 1.0 {
906+
Self::NAN
907+
} else {
908+
(self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln()
909+
}
904910
}
905911

906912
/// Inverse hyperbolic tangent function.

library/std/src/f32/tests.rs

+8
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,11 @@ fn test_asinh() {
587587
assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32);
588588
// regression test for the catastrophic cancellation fixed in 72486
589589
assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32);
590+
591+
// test for low accuracy from issue 104548
592+
assert_approx_eq!(60.0f32, 60.0f32.sinh().asinh());
593+
// mul needed for approximate comparison to be meaningful
594+
assert_approx_eq!(1.0f32, 1e-15f32.sinh().asinh() * 1e15f32);
590595
}
591596

592597
#[test]
@@ -602,6 +607,9 @@ fn test_acosh() {
602607
assert!(nan.acosh().is_nan());
603608
assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32);
604609
assert_approx_eq!(3.0f32.acosh(), 1.76274717403908605046521864995958461f32);
610+
611+
// test for low accuracy from issue 104548
612+
assert_approx_eq!(60.0f32, 60.0f32.cosh().acosh());
605613
}
606614

607615
#[test]

library/std/src/f64.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,9 @@ impl f64 {
882882
#[stable(feature = "rust1", since = "1.0.0")]
883883
#[inline]
884884
pub fn asinh(self) -> f64 {
885-
(self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self)
885+
let ax = self.abs();
886+
let ix = 1.0 / ax;
887+
(ax + (ax / (Self::hypot(1.0, ix) + ix))).ln_1p().copysign(self)
886888
}
887889

888890
/// Inverse hyperbolic cosine function.
@@ -902,7 +904,11 @@ impl f64 {
902904
#[stable(feature = "rust1", since = "1.0.0")]
903905
#[inline]
904906
pub fn acosh(self) -> f64 {
905-
if self < 1.0 { Self::NAN } else { (self + ((self * self) - 1.0).sqrt()).ln() }
907+
if self < 1.0 {
908+
Self::NAN
909+
} else {
910+
(self + ((self - 1.0).sqrt() * (self + 1.0).sqrt())).ln()
911+
}
906912
}
907913

908914
/// Inverse hyperbolic tangent function.

library/std/src/f64/tests.rs

+8
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,11 @@ fn test_asinh() {
575575
assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64);
576576
// regression test for the catastrophic cancellation fixed in 72486
577577
assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083);
578+
579+
// test for low accuracy from issue 104548
580+
assert_approx_eq!(60.0f64, 60.0f64.sinh().asinh());
581+
// mul needed for approximate comparison to be meaningful
582+
assert_approx_eq!(1.0f64, 1e-15f64.sinh().asinh() * 1e15f64);
578583
}
579584

580585
#[test]
@@ -590,6 +595,9 @@ fn test_acosh() {
590595
assert!(nan.acosh().is_nan());
591596
assert_approx_eq!(2.0f64.acosh(), 1.31695789692481670862504634730796844f64);
592597
assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64);
598+
599+
// test for low accuracy from issue 104548
600+
assert_approx_eq!(60.0f64, 60.0f64.cosh().acosh());
593601
}
594602

595603
#[test]

0 commit comments

Comments
 (0)