1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
//===-- Half-precision 10^x function --------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "src/math/exp10f16.h"
#include "expxf16.h"
#include "hdr/errno_macros.h"
#include "hdr/fenv_macros.h"
#include "src/__support/CPP/array.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/cast.h"
#include "src/__support/FPUtil/except_value_utils.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/FPUtil/nearest_integer.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"
#include "src/__support/macros/properties/cpu_features.h"
namespace LIBC_NAMESPACE_DECL {
#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
#ifdef LIBC_TARGET_CPU_HAS_FMA_FLOAT
static constexpr size_t N_EXP10F16_EXCEPTS = 5;
#else
static constexpr size_t N_EXP10F16_EXCEPTS = 8;
#endif
static constexpr fputil::ExceptValues<float16, N_EXP10F16_EXCEPTS>
EXP10F16_EXCEPTS = {{
// x = 0x1.8f4p-2, exp10f16(x) = 0x1.3ap+1 (RZ)
{0x363dU, 0x40e8U, 1U, 0U, 1U},
// x = 0x1.95cp-2, exp10f16(x) = 0x1.3ecp+1 (RZ)
{0x3657U, 0x40fbU, 1U, 0U, 0U},
// x = -0x1.018p-4, exp10f16(x) = 0x1.bbp-1 (RZ)
{0xac06U, 0x3aecU, 1U, 0U, 0U},
// x = -0x1.c28p+0, exp10f16(x) = 0x1.1ccp-6 (RZ)
{0xbf0aU, 0x2473U, 1U, 0U, 0U},
// x = -0x1.e1cp+1, exp10f16(x) = 0x1.694p-13 (RZ)
{0xc387U, 0x09a5U, 1U, 0U, 0U},
#ifndef LIBC_TARGET_CPU_HAS_FMA_FLOAT
// x = 0x1.0cp+1, exp10f16(x) = 0x1.f04p+6 (RZ)
{0x4030U, 0x57c1U, 1U, 0U, 1U},
// x = 0x1.1b8p+1, exp10f16(x) = 0x1.47cp+7 (RZ)
{0x406eU, 0x591fU, 1U, 0U, 1U},
// x = 0x1.1b8p+2, exp10f16(x) = 0x1.a4p+14 (RZ)
{0x446eU, 0x7690U, 1U, 0U, 1U},
#endif
}};
#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
LLVM_LIBC_FUNCTION(float16, exp10f16, (float16 x)) {
using FPBits = fputil::FPBits<float16>;
FPBits x_bits(x);
uint16_t x_u = x_bits.uintval();
uint16_t x_abs = x_u & 0x7fffU;
// When |x| >= 5, or x is NaN.
if (LIBC_UNLIKELY(x_abs >= 0x4500U)) {
// exp10(NaN) = NaN
if (x_bits.is_nan()) {
if (x_bits.is_signaling_nan()) {
fputil::raise_except_if_required(FE_INVALID);
return FPBits::quiet_nan().get_val();
}
return x;
}
// When x >= 5.
if (x_bits.is_pos()) {
// exp10(+inf) = +inf
if (x_bits.is_inf())
return FPBits::inf().get_val();
switch (fputil::quick_get_round()) {
case FE_TONEAREST:
case FE_UPWARD:
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);
return FPBits::inf().get_val();
default:
return FPBits::max_normal().get_val();
}
}
// When x <= -8.
if (x_u >= 0xc800U) {
// exp10(-inf) = +0
if (x_bits.is_inf())
return FPBits::zero().get_val();
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
if (fputil::fenv_is_round_up())
return FPBits::min_subnormal().get_val();
return FPBits::zero().get_val();
}
}
// When x is 1, 2, 3, or 4. These are hard-to-round cases with exact results.
if (LIBC_UNLIKELY((x_u & ~(0x3c00U | 0x4000U | 0x4200U | 0x4400U)) == 0)) {
switch (x_u) {
case 0x3c00U: // x = 1.0f16
return fputil::cast<float16>(10.0);
case 0x4000U: // x = 2.0f16
return fputil::cast<float16>(100.0);
case 0x4200U: // x = 3.0f16
return fputil::cast<float16>(1'000.0);
case 0x4400U: // x = 4.0f16
return fputil::cast<float16>(10'000.0);
}
}
#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
if (auto r = EXP10F16_EXCEPTS.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
return r.value();
#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
// 10^x = 2^((hi + mid) * log2(10)) * 10^lo
auto [exp2_hi_mid, exp10_lo] = exp10_range_reduction(x);
return fputil::cast<float16>(exp2_hi_mid * exp10_lo);
}
} // namespace LIBC_NAMESPACE_DECL
|