Skip to content

Commit 4ff508d

Browse files
committed
fixup! [libc][math][c23] Add f16divf C23 math function
1 parent df87e20 commit 4ff508d

File tree

5 files changed

+174
-22
lines changed

5 files changed

+174
-22
lines changed

libc/src/__support/FPUtil/generic/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@ add_header_library(
5151
HDRS
5252
div.h
5353
DEPENDS
54+
libc.hdr.errno_macros
5455
libc.hdr.fenv_macros
5556
libc.src.__support.CPP.bit
5657
libc.src.__support.CPP.type_traits
58+
libc.src.__support.FPUtil.basic_operations
5759
libc.src.__support.FPUtil.fenv_impl
5860
libc.src.__support.FPUtil.fp_bits
5961
libc.src.__support.FPUtil.dyadic_float

libc/src/__support/FPUtil/generic/div.h

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_DIV_H
1010
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_GENERIC_DIV_H
1111

12+
#include "hdr/errno_macros.h"
1213
#include "hdr/fenv_macros.h"
1314
#include "src/__support/CPP/bit.h"
1415
#include "src/__support/CPP/type_traits.h"
16+
#include "src/__support/FPUtil/BasicOperations.h"
1517
#include "src/__support/FPUtil/FEnvImpl.h"
1618
#include "src/__support/FPUtil/FPBits.h"
1719
#include "src/__support/FPUtil/dyadic_float.h"
@@ -53,12 +55,28 @@ div(InType x, InType y) {
5355
if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
5456
raise_except_if_required(FE_INVALID);
5557

56-
// TODO: Handle NaN payloads.
58+
if (x_bits.is_quiet_nan()) {
59+
InStorageType x_payload = static_cast<InStorageType>(getpayload(x));
60+
if ((x_payload & ~(OutFPBits::FRACTION_MASK >> 1)) == 0)
61+
return OutFPBits::quiet_nan(x_bits.sign(),
62+
static_cast<OutStorageType>(x_payload))
63+
.get_val();
64+
}
65+
66+
if (y_bits.is_quiet_nan()) {
67+
InStorageType y_payload = static_cast<InStorageType>(getpayload(y));
68+
if ((y_payload & ~(OutFPBits::FRACTION_MASK >> 1)) == 0)
69+
return OutFPBits::quiet_nan(y_bits.sign(),
70+
static_cast<OutStorageType>(y_payload))
71+
.get_val();
72+
}
73+
5774
return OutFPBits::quiet_nan().get_val();
5875
}
5976

6077
if (x_bits.is_inf()) {
6178
if (y_bits.is_inf()) {
79+
set_errno_if_required(EDOM);
6280
raise_except_if_required(FE_INVALID);
6381
return OutFPBits::quiet_nan().get_val();
6482
}
@@ -91,21 +109,40 @@ div(InType x, InType y) {
91109
would_q_be_subnormal;
92110

93111
if (q_exponent + OutFPBits::EXP_BIAS >= OutFPBits::MAX_BIASED_EXPONENT) {
94-
switch (quick_get_round()) {
112+
set_errno_if_required(ERANGE);
113+
raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
114+
115+
switch (get_round()) {
95116
case FE_TONEAREST:
117+
return OutFPBits::inf(result_sign).get_val();
118+
case FE_DOWNWARD:
119+
if (result_sign.is_pos())
120+
return OutFPBits::max_normal(result_sign).get_val();
121+
return OutFPBits::inf(result_sign).get_val();
96122
case FE_UPWARD:
97-
return OutFPBits::inf().get_val();
123+
if (result_sign.is_pos())
124+
return OutFPBits::inf(result_sign).get_val();
125+
return OutFPBits::max_normal(result_sign).get_val();
98126
default:
99-
return OutFPBits::max_normal().get_val();
127+
return OutFPBits::max_normal(result_sign).get_val();
100128
}
101129
}
102130

103131
if (q_exponent < -OutFPBits::EXP_BIAS - OutFPBits::FRACTION_LEN) {
132+
set_errno_if_required(ERANGE);
133+
raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
134+
104135
switch (quick_get_round()) {
136+
case FE_DOWNWARD:
137+
if (result_sign.is_pos())
138+
return OutFPBits::zero(result_sign).get_val();
139+
return OutFPBits::min_subnormal(result_sign).get_val();
105140
case FE_UPWARD:
106-
return OutFPBits::min_subnormal().get_val();
141+
if (result_sign.is_pos())
142+
return OutFPBits::min_subnormal(result_sign).get_val();
143+
return OutFPBits::zero(result_sign).get_val();
107144
default:
108-
return OutFPBits::zero().get_val();
145+
return OutFPBits::zero(result_sign).get_val();
109146
}
110147
}
111148

@@ -134,8 +171,10 @@ div(InType x, InType y) {
134171
if (q_exponent > -OutFPBits::EXP_BIAS) {
135172
// Result is normal.
136173

137-
round = (q & (InStorageType(1) << (Q_EXTRA_FRACTION_LEN - 1))) != 0;
138-
sticky = (q & ((InStorageType(1) << (Q_EXTRA_FRACTION_LEN - 1)) - 1)) != 0;
174+
InStorageType round_mask = InStorageType(1) << (Q_EXTRA_FRACTION_LEN - 1);
175+
round = (q & round_mask) != 0;
176+
InStorageType sticky_mask = round_mask - 1;
177+
sticky = (q & sticky_mask) != 0;
139178

140179
result = OutFPBits::create_value(
141180
result_sign,
@@ -147,21 +186,23 @@ div(InType x, InType y) {
147186
// Result is subnormal.
148187

149188
// +1 because the leading bit is now part of the fraction.
150-
int underflow_extra_fraction_len =
189+
int extra_fraction_len =
151190
Q_EXTRA_FRACTION_LEN + 1 - q_exponent - OutFPBits::EXP_BIAS;
152191

153-
InStorageType round_bit_mask = InStorageType(1)
154-
<< (underflow_extra_fraction_len - 1);
155-
round = (q & round_bit_mask) != 0;
156-
InStorageType sticky_bits_mask = round_bit_mask - 1;
157-
sticky = (q & sticky_bits_mask) != 0;
192+
InStorageType round_mask = InStorageType(1) << (extra_fraction_len - 1);
193+
round = (q & round_mask) != 0;
194+
InStorageType sticky_mask = round_mask - 1;
195+
sticky = (q & sticky_mask) != 0;
158196

159197
result = OutFPBits::create_value(
160198
result_sign, 0,
161-
static_cast<OutStorageType>(q >> underflow_extra_fraction_len))
199+
static_cast<OutStorageType>(q >> extra_fraction_len))
162200
.uintval();
163201
}
164202

203+
if (round || sticky)
204+
raise_except_if_required(FE_INEXACT);
205+
165206
bool lsb = (result & 1) != 0;
166207

167208
switch (quick_get_round()) {

libc/test/src/math/DivTest.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class DivTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
3636
InFPBits::min_subnormal().uintval();
3737

3838
public:
39-
typedef OutType (*DivFunc)(InType, InType);
39+
using DivFunc = OutType (*)(InType, InType);
4040

4141
void test_subnormal_range(DivFunc func) {
4242
constexpr InStorageType COUNT = 100'001;

libc/test/src/math/smoke/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3640,6 +3640,7 @@ add_fp_unittest(
36403640
DivTest.h
36413641
DEPENDS
36423642
libc.hdr.fenv_macros
3643+
libc.src.__support.FPUtil.basic_operations
36433644
libc.src.math.f16divf
36443645
)
36453646

libc/test/src/math/smoke/DivTest.h

Lines changed: 114 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,49 @@
1010
#define LLVM_LIBC_TEST_SRC_MATH_SMOKE_DIVTEST_H
1111

1212
#include "hdr/fenv_macros.h"
13+
#include "src/__support/FPUtil/BasicOperations.h"
1314
#include "test/UnitTest/FEnvSafeTest.h"
1415
#include "test/UnitTest/FPMatcher.h"
16+
#include "test/UnitTest/RoundingModeUtils.h"
1517
#include "test/UnitTest/Test.h"
1618

1719
template <typename OutType, typename InType>
1820
class DivTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
1921

2022
DECLARE_SPECIAL_CONSTANTS(OutType)
2123

24+
struct InConstants {
25+
DECLARE_SPECIAL_CONSTANTS(InType)
26+
};
27+
28+
using InFPBits = typename InConstants::FPBits;
29+
using InStorageType = typename InConstants::StorageType;
30+
2231
public:
23-
typedef OutType (*DivFunc)(InType, InType);
32+
using DivFunc = OutType (*)(InType, InType);
2433

2534
void test_special_numbers(DivFunc func) {
2635
EXPECT_FP_IS_NAN(func(aNaN, aNaN));
36+
EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(sNaN, sNaN), FE_INVALID);
2737

28-
EXPECT_FP_EQ(inf, func(inf, zero));
29-
EXPECT_FP_EQ(neg_inf, func(neg_inf, zero));
30-
EXPECT_FP_EQ(neg_inf, func(inf, neg_zero));
31-
EXPECT_FP_EQ(inf, func(neg_inf, neg_zero));
38+
InType qnan_42 = InFPBits::quiet_nan(Sign::POS, 0x42).get_val();
39+
EXPECT_FP_EQ(InType(0x42.0p+0),
40+
LIBC_NAMESPACE::fputil::getpayload(func(qnan_42, zero)));
41+
EXPECT_FP_EQ(InType(0x42.0p+0),
42+
LIBC_NAMESPACE::fputil::getpayload(func(zero, qnan_42)));
43+
44+
if constexpr (sizeof(OutType) < sizeof(InType)) {
45+
InStorageType max_payload = InFPBits::FRACTION_MASK >> 1;
46+
InType qnan_max = InFPBits::quiet_nan(Sign::POS, max_payload).get_val();
47+
EXPECT_FP_EQ(zero,
48+
LIBC_NAMESPACE::fputil::getpayload(func(qnan_max, zero)));
49+
EXPECT_FP_EQ(zero,
50+
LIBC_NAMESPACE::fputil::getpayload(func(zero, qnan_max)));
51+
EXPECT_FP_EQ(InType(0x42.0p+0),
52+
LIBC_NAMESPACE::fputil::getpayload(func(qnan_max, qnan_42)));
53+
EXPECT_FP_EQ(InType(0x42.0p+0),
54+
LIBC_NAMESPACE::fputil::getpayload(func(qnan_42, qnan_max)));
55+
}
3256

3357
EXPECT_FP_EQ(inf, func(inf, zero));
3458
EXPECT_FP_EQ(neg_inf, func(neg_inf, zero));
@@ -52,16 +76,100 @@ class DivTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
5276
EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(neg_zero, neg_zero), FE_INVALID);
5377

5478
EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(inf, inf), FE_INVALID);
79+
EXPECT_MATH_ERRNO(EDOM);
5580
EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(neg_inf, inf), FE_INVALID);
81+
EXPECT_MATH_ERRNO(EDOM);
5682
EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(inf, neg_inf), FE_INVALID);
83+
EXPECT_MATH_ERRNO(EDOM);
5784
EXPECT_FP_IS_NAN_WITH_EXCEPTION(func(neg_inf, neg_inf), FE_INVALID);
85+
EXPECT_MATH_ERRNO(EDOM);
86+
}
87+
88+
void test_range_errors(DivFunc func) {
89+
using namespace LIBC_NAMESPACE::fputil::testing;
90+
91+
if (ForceRoundingMode r(RoundingMode::Nearest); r.success) {
92+
EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(max_normal, min_normal),
93+
FE_OVERFLOW | FE_INEXACT);
94+
EXPECT_MATH_ERRNO(ERANGE);
95+
EXPECT_FP_EQ_WITH_EXCEPTION(-inf, func(neg_max_normal, min_denormal),
96+
FE_OVERFLOW | FE_INEXACT);
97+
EXPECT_MATH_ERRNO(ERANGE);
98+
99+
EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(min_denormal, max_normal),
100+
FE_UNDERFLOW | FE_INEXACT);
101+
EXPECT_MATH_ERRNO(ERANGE);
102+
EXPECT_FP_EQ_WITH_EXCEPTION(neg_zero, func(neg_min_denormal, max_normal),
103+
FE_UNDERFLOW | FE_INEXACT);
104+
EXPECT_MATH_ERRNO(ERANGE);
105+
}
106+
107+
if (ForceRoundingMode r(RoundingMode::TowardZero); r.success) {
108+
EXPECT_FP_EQ_WITH_EXCEPTION(max_normal, func(max_normal, min_normal),
109+
FE_OVERFLOW | FE_INEXACT);
110+
EXPECT_MATH_ERRNO(ERANGE);
111+
EXPECT_FP_EQ_WITH_EXCEPTION(neg_max_normal,
112+
func(neg_max_normal, min_denormal),
113+
FE_OVERFLOW | FE_INEXACT);
114+
EXPECT_MATH_ERRNO(ERANGE);
115+
116+
EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(min_denormal, max_normal),
117+
FE_UNDERFLOW | FE_INEXACT);
118+
EXPECT_MATH_ERRNO(ERANGE);
119+
EXPECT_FP_EQ_WITH_EXCEPTION(neg_zero, func(neg_min_denormal, max_normal),
120+
FE_UNDERFLOW | FE_INEXACT);
121+
EXPECT_MATH_ERRNO(ERANGE);
122+
}
123+
124+
if (ForceRoundingMode r(RoundingMode::Downward); r.success) {
125+
EXPECT_FP_EQ_WITH_EXCEPTION(max_normal, func(max_normal, min_normal),
126+
FE_OVERFLOW | FE_INEXACT);
127+
EXPECT_MATH_ERRNO(ERANGE);
128+
EXPECT_FP_EQ_WITH_EXCEPTION(-inf, func(neg_max_normal, min_denormal),
129+
FE_OVERFLOW | FE_INEXACT);
130+
EXPECT_MATH_ERRNO(ERANGE);
131+
132+
EXPECT_FP_EQ_WITH_EXCEPTION(zero, func(min_denormal, max_normal),
133+
FE_UNDERFLOW | FE_INEXACT);
134+
EXPECT_MATH_ERRNO(ERANGE);
135+
EXPECT_FP_EQ_WITH_EXCEPTION(neg_min_denormal,
136+
func(neg_min_denormal, max_normal),
137+
FE_UNDERFLOW | FE_INEXACT);
138+
EXPECT_MATH_ERRNO(ERANGE);
139+
}
140+
141+
if (ForceRoundingMode r(RoundingMode::Upward); r.success) {
142+
EXPECT_FP_EQ_WITH_EXCEPTION(inf, func(max_normal, min_normal),
143+
FE_OVERFLOW | FE_INEXACT);
144+
EXPECT_MATH_ERRNO(ERANGE);
145+
EXPECT_FP_EQ_WITH_EXCEPTION(neg_max_normal,
146+
func(neg_max_normal, min_denormal),
147+
FE_OVERFLOW | FE_INEXACT);
148+
EXPECT_MATH_ERRNO(ERANGE);
149+
150+
EXPECT_FP_EQ_WITH_EXCEPTION(min_denormal, func(min_denormal, max_normal),
151+
FE_UNDERFLOW | FE_INEXACT);
152+
EXPECT_MATH_ERRNO(ERANGE);
153+
EXPECT_FP_EQ_WITH_EXCEPTION(neg_zero, func(neg_min_denormal, max_normal),
154+
FE_UNDERFLOW | FE_INEXACT);
155+
EXPECT_MATH_ERRNO(ERANGE);
156+
}
157+
}
158+
159+
void test_inexact_results(DivFunc func) {
160+
func(InType(1.0), InType(3.0));
161+
EXPECT_FP_EXCEPTION(FE_INEXACT);
58162
}
59163
};
60164

61165
#define LIST_DIV_TESTS(OutType, InType, func) \
62166
using LlvmLibcDivTest = DivTest<OutType, InType>; \
63167
TEST_F(LlvmLibcDivTest, SpecialNumbers) { test_special_numbers(&func); } \
64168
TEST_F(LlvmLibcDivTest, DivisionByZero) { test_division_by_zero(&func); } \
65-
TEST_F(LlvmLibcDivTest, InvalidOperations) { test_invalid_operations(&func); }
169+
TEST_F(LlvmLibcDivTest, InvalidOperations) { \
170+
test_invalid_operations(&func); \
171+
} \
172+
TEST_F(LlvmLibcDivTest, RangeErrors) { test_range_errors(&func); } \
173+
TEST_F(LlvmLibcDivTest, InexactResults) { test_inexact_results(&func); }
66174

67175
#endif // LLVM_LIBC_TEST_SRC_MATH_SMOKE_DIVTEST_H

0 commit comments

Comments
 (0)