asinh(x) = log(x + sqrt(x^2 + 1)) If x^2 will overflow and x is positive, we can approximate x + sqrt(x^2 + 1) as 2*x and return log(2) + log(x). If x is negative, the above would give us some trouble; we can't approximate the result as x + abs(x) = 0! But we're saved by the fact that asinh(-x) = -asinh(x).
| 1182 | // the result as x + abs(x) = 0! But we're saved by the fact that asinh(-x) = |
| 1183 | // -asinh(x). |
| 1184 | XlaOp Asinh(XlaOp x) { |
| 1185 | XlaBuilder* b = x.builder(); |
| 1186 | auto do_it = [&](XlaOp x) -> StatusOr<XlaOp> { |
| 1187 | TF_ASSIGN_OR_RETURN(auto shape, b->GetShape(x)); |
| 1188 | auto one = ScalarLike(x, 1); |
| 1189 | |
| 1190 | // Let a = abs(x). Compute |
| 1191 | // |
| 1192 | // y = log(a + sqrt(a*a + 1)) if a < sqrt_max_value, or |
| 1193 | // y = log(a) + log(2) otherwise |
| 1194 | // |
| 1195 | // and then return |
| 1196 | // |
| 1197 | // y * sign(x). |
| 1198 | // |
| 1199 | // TODO(jlebar): For now, we ignore the question of overflow if x is a |
| 1200 | // complex type, because we don't yet have exhaustive tests for complex trig |
| 1201 | // functions. |
| 1202 | if (primitive_util::IsComplexType(shape.element_type())) { |
| 1203 | return Log(x + Sqrt(x * x + one)); |
| 1204 | } |
| 1205 | // For small x, sqrt(x**2 + 1) will evaluate to 1 due to floating point |
| 1206 | // arithmetic. However, we would like to retain the low order term of this, |
| 1207 | // which is around 0.5 * x**2 using a binomial expansion. |
| 1208 | // Let z = sqrt(a**2 + 1) |
| 1209 | // log(a + sqrt(a**2 + 1)) = |
| 1210 | // log((a + sqrt(a**2 + 1)) * (1 + sqrt(a**2 + 1)) / (1 + sqrt(a**2 + 1))) = |
| 1211 | // log((a + a**2 + 1 + a * z + z) / (1 + z)) = |
| 1212 | // log(1 + a + a**2 / (1 + z)) = |
| 1213 | // log(1 + a + a ** 2 / (1 + sqrt(a**2 + 1))) |
| 1214 | // This rewrite retains the lower order term. |
| 1215 | auto a = Abs(x); |
| 1216 | auto small_result = Log1p(a + a * a / (one + Sqrt(a * a + one))); |
| 1217 | auto naive_result = Log(a + Sqrt(a * a + one)); |
| 1218 | auto overflow_result = Log(Abs(a)) + Log(ScalarLike(a, 2)); |
| 1219 | auto sqrt_max_value = Sqrt(MaxFiniteValue(b, shape.element_type())); |
| 1220 | return Sign(x) * Select(Ge(a, sqrt_max_value), overflow_result, |
| 1221 | Select(Le(a, one), small_result, naive_result)); |
| 1222 | }; |
| 1223 | // These upcasts are not strictly necessary on all platforms to get within our |
| 1224 | // error tolerances, so we could relax this if it ever mattered. |
| 1225 | return DoWithUpcastToF32(x, {BF16, F16}, [&](XlaOp x) { |
| 1226 | return b->ReportErrorOrReturn(do_it(x)); |
| 1227 | }); |
| 1228 | } |
| 1229 | |
| 1230 | // atanh(x) = 0.5 * log((1 + x) / (1 - x)) if abs(x) <= 1 |
| 1231 | // atanh(x) = nan otherwise |