Skip to content

Commit 730f736

Browse files
committed
Fix asinh of negative values
When `x` has large magnitude, `x + ((x * x) + 1.0).sqrt()` approaches `x + x.abs()`. For negative values of `x`, this leads to catastrophic cancellation, resulting in large errors or even 0 being passed to `ln`, producing incorrect results including `-inf`. Becuase asinh is an odd function, i.e. -asinh(x) = asinh(-x) for all x, we can avoid the catastrophic cancellation and obtain correct results by taking the absolute value of `self` for the first term. `self * self` is always positive, so in effect this gives us `x.abs().asinh().copysign(x)` which as discussed above is algebraically equivalent, but is much more accurate.
1 parent 215f2d3 commit 730f736

File tree

2 files changed

+6
-2
lines changed

2 files changed

+6
-2
lines changed

Diff for: src/libstd/f32.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,7 @@ impl f32 {
835835
if self == Self::NEG_INFINITY {
836836
Self::NEG_INFINITY
837837
} else {
838-
(self + ((self * self) + 1.0).sqrt()).ln().copysign(self)
838+
(self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self)
839839
}
840840
}
841841

@@ -1414,6 +1414,8 @@ mod tests {
14141414
assert!((-0.0f32).asinh().is_sign_negative()); // issue 63271
14151415
assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32);
14161416
assert_approx_eq!((-2.0f32).asinh(), -1.443635475178810342493276740273105f32);
1417+
// regression test for the catastrophic cancellation fixed in 72486
1418+
assert_approx_eq!((-3000.0f32).asinh(), -8.699514775987968673236893537700647f32);
14171419
}
14181420

14191421
#[test]

Diff for: src/libstd/f64.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,7 @@ impl f64 {
837837
if self == Self::NEG_INFINITY {
838838
Self::NEG_INFINITY
839839
} else {
840-
(self + ((self * self) + 1.0).sqrt()).ln().copysign(self)
840+
(self.abs() + ((self * self) + 1.0).sqrt()).ln().copysign(self)
841841
}
842842
}
843843

@@ -1443,6 +1443,8 @@ mod tests {
14431443
// issue 63271
14441444
assert_approx_eq!(2.0f64.asinh(), 1.443635475178810342493276740273105f64);
14451445
assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64);
1446+
// regression test for the catastrophic cancellation fixed in 72486
1447+
assert_approx_eq!((-67452098.07139316f64).asinh(), -18.72007542627454439398548429400083);
14461448
}
14471449

14481450
#[test]

0 commit comments

Comments
 (0)