|
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