|
33 | 33 | #include "stdlib/math/base/special/atanh.h" |
34 | 34 | #include "stdlib/math/base/assert/is_nan.h" |
35 | 35 | #include "stdlib/math/base/special/log1p.h" |
| 36 | +#include "stdlib/constants/float64/high_word_abs_mask.h" |
| 37 | +#include "stdlib/number/float64/base/set_high_word.h" |
| 38 | +#include "stdlib/number/float64/base/to_words.h" |
36 | 39 | #include "stdlib/constants/float64/pinf.h" |
37 | 40 | #include "stdlib/constants/float64/ninf.h" |
38 | 41 | #include <stdint.h> |
39 | 42 |
|
40 | | -static const double NEAR_ZERO = 1.0 / (1 << 28); // 2**-28 |
| 43 | +static const double one = 1.0; |
| 44 | + |
| 45 | +static const double zero = 0.0; |
| 46 | + |
| 47 | +static const double huge = 1e300; |
| 48 | + |
| 49 | +// 0x3ff00000 = 1072693248 => 0 01111111111 00000000000000000000 => biased exponent: 1023 = 0+1023 => 2^0 = 1 |
| 50 | +static const int32_t HIGH_BIASED_EXP_0 = 0x3ff00000; |
| 51 | + |
| 52 | +// 0x3e300000 = 1040187392 => 0 01111100011 00000000000000000000 => biased exponent: 100 = -23+127 |
| 53 | +static const int32_t HIGH_BIASED_EXP_NEG_7 = 0x3e300000; |
| 54 | + |
| 55 | +// 0x3fe00000 = 1071644672 => 0 01111111110 00000000000000000000 => biased exponent: 1022 = -1+1023 => 2^-1 |
| 56 | +static const int32_t HIGH_BIASED_EXP_NEG_1 = 0x3fe00000; |
41 | 57 |
|
42 | 58 | /** |
43 | 59 | * Computes the hyperbolic arctangent of a double-precision floating-point number. |
@@ -79,34 +95,34 @@ static const double NEAR_ZERO = 1.0 / (1 << 28); // 2**-28 |
79 | 95 | * // returns ~1.472 |
80 | 96 | */ |
81 | 97 | double stdlib_base_atanh( const double x ) { |
82 | | - int32_t sgn; |
83 | | - double ax; |
| 98 | + u_int32_t lx; |
| 99 | + double hx; |
| 100 | + double ahx; |
84 | 101 | double t; |
85 | 102 | if ( stdlib_base_is_nan( x ) || x < -1.0 || x > 1.0 ) { |
86 | 103 | return 0.0 / 0.0; // NaN |
87 | 104 | } |
88 | | - if ( x == 1.0 ) { |
89 | | - return STDLIB_CONSTANT_FLOAT64_PINF; |
| 105 | + // Split `x` into high and low words: |
| 106 | + stdlib_base_float64_to_words( x, &hx, &lx ); |
| 107 | + |
| 108 | + ahx = hx & STDLIB_CONSTANT_FLOAT64_HIGH_WORD_ABS_MASK; |
| 109 | + |
| 110 | + if( ( ahx | ( ( lx | (-lx) )>>31 ))> HIGH_BIASED_EXP_0 ) { |
| 111 | + return ( x - x ) / ( x - x ); // |x|>1 |
90 | 112 | } |
91 | | - if ( x == -1.0 ) { |
92 | | - return STDLIB_CONSTANT_FLOAT64_NINF; |
| 113 | + if( ahx == HIGH_BIASED_EXP_0 ) { |
| 114 | + return x / zero; |
93 | 115 | } |
94 | | - if ( x < 0.0 ) { |
95 | | - sgn = 1; |
96 | | - ax = -x; |
97 | | - } else { |
98 | | - sgn = 0; |
99 | | - ax = x; |
| 116 | + if( ahx < HIGH_BIASED_EXP_NEG_7 && ( huge+x ) > zero) { |
| 117 | + return x; // x<2**-28 |
100 | 118 | } |
101 | | - // Case: |x| < 2**-28 |
102 | | - if ( ax < NEAR_ZERO ) { |
103 | | - return ( sgn == 1 ) ? -ax : ax; |
104 | | - } |
105 | | - if ( ax < 0.5 ) { |
106 | | - t = ax + ax; |
107 | | - t = 0.5 * stdlib_base_log1p( t + ( t * ax / ( 1 - ax ) ) ); |
| 119 | + stdlib_base_float64_set_high_word( x, &ahx ); |
| 120 | + if( ahx < HIGH_BIASED_EXP_NEG_1 ) { |
| 121 | + t = x + x; |
| 122 | + t = 0.5 * stdlib_base_log1p( t + ( t * x / ( one - x ) ) ); |
| 123 | + |
108 | 124 | } else { |
109 | | - t = 0.5 * stdlib_base_log1p( ( ax + ax ) / ( 1 - ax ) ); |
| 125 | + t = 0.5 * stdlib_base_log1p( ( x + x ) / ( one - x ) ); |
110 | 126 | } |
111 | | - return ( sgn == 1 ) ? -t : t; |
| 127 | + return ( hx >= 0 ) ? -t : t; |
112 | 128 | } |
0 commit comments