Skip to content

Commit 9f5b06f

Browse files
committed
[C++] Complete std::numeric_limits<Float16> specialization
1 parent d2315fe commit 9f5b06f

2 files changed

Lines changed: 95 additions & 2 deletions

File tree

cpp/src/arrow/util/float16.h

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,22 +185,51 @@ static_assert(sizeof(Float16) == sizeof(uint16_t));
185185
} // namespace util
186186
} // namespace arrow
187187

188-
// TODO: Not complete
189188
template <>
190189
class std::numeric_limits<arrow::util::Float16> {
191190
using T = arrow::util::Float16;
192191

193192
public:
194193
static constexpr bool is_specialized = true;
195194
static constexpr bool is_signed = true;
195+
static constexpr bool is_integer = false;
196+
static constexpr bool is_exact = false;
196197
static constexpr bool has_infinity = true;
197198
static constexpr bool has_quiet_NaN = true;
199+
static constexpr bool has_signaling_NaN = true;
200+
static constexpr std::float_denorm_style has_denorm = std::denorm_present;
201+
static constexpr bool has_denorm_loss = false;
202+
static constexpr bool is_iec559 = true;
203+
static constexpr bool is_bounded = true;
204+
static constexpr bool is_modulo = false;
205+
static constexpr int radix = 2;
206+
207+
// Float16 has 10 explicit mantissa bits + 1 implicit bit = 11 bits precision
208+
static constexpr int digits = 11;
209+
// Number of decimal digits that can be represented: floor(10 * log10(2))
210+
static constexpr int digits10 = 3;
211+
// Number of decimal digits to fully represent the type: ceil(11 * log10(2) + 1)
212+
static constexpr int max_digits10 = 5;
213+
214+
// Exponent range: bias = 15, min subnormal exponent = -14, min normal = -13
215+
static constexpr int min_exponent = -13;
216+
static constexpr int min_exponent10 = -4;
217+
// Max exponent before infinity: field value 30 -> 30 - 15 + 1 = 16
218+
static constexpr int max_exponent = 16;
219+
static constexpr int max_exponent10 = 4;
220+
221+
static constexpr bool traps = false;
222+
static constexpr bool tinyness_before = false;
223+
static constexpr std::float_round_style round_style = std::round_to_nearest;
198224

199225
static constexpr T min() { return T::FromBits(0b0000010000000000); }
200226
static constexpr T max() { return T::FromBits(0b0111101111111111); }
201227
static constexpr T lowest() { return -max(); }
228+
static constexpr T epsilon() { return T::FromBits(0b0001010000000000); } // 2^-10
229+
static constexpr T round_error() { return T::FromBits(0b0011100000000000); } // 0.5
230+
static constexpr T denorm_min() { return T::FromBits(0b0000000000000001); }
202231

203232
static constexpr T infinity() { return T::FromBits(0b0111110000000000); }
204-
205233
static constexpr T quiet_NaN() { return T::FromBits(0b0111111111111111); }
234+
static constexpr T signaling_NaN() { return T::FromBits(0b0111110000000001); }
206235
};

cpp/src/arrow/util/float16_test.cc

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,5 +372,69 @@ TEST(Float16Test, FromBytes) {
372372
ASSERT_EQ(Float16::FromBigEndian(bytes.data()), Float16::FromBits(0x1cd0));
373373
}
374374

375+
TEST(Float16Test, NumericLimits) {
376+
using F16 = std::numeric_limits<Float16>;
377+
using F32 = std::numeric_limits<float>;
378+
379+
// Boolean traits - should match standard float
380+
ASSERT_EQ(F16::is_specialized, F32::is_specialized);
381+
ASSERT_EQ(F16::is_signed, F32::is_signed);
382+
ASSERT_EQ(F16::is_integer, F32::is_integer);
383+
ASSERT_EQ(F16::is_exact, F32::is_exact);
384+
ASSERT_EQ(F16::has_infinity, F32::has_infinity);
385+
ASSERT_EQ(F16::has_quiet_NaN, F32::has_quiet_NaN);
386+
ASSERT_EQ(F16::has_signaling_NaN, F32::has_signaling_NaN);
387+
ASSERT_EQ(F16::has_denorm, F32::has_denorm);
388+
ASSERT_EQ(F16::has_denorm_loss, F32::has_denorm_loss);
389+
ASSERT_EQ(F16::is_iec559, F32::is_iec559);
390+
ASSERT_EQ(F16::is_bounded, F32::is_bounded);
391+
ASSERT_EQ(F16::is_modulo, F32::is_modulo);
392+
ASSERT_EQ(F16::radix, F32::radix);
393+
394+
// Check if IEEE 754 is implemented correctly.
395+
// Precision and exponent range
396+
ASSERT_EQ(F16::digits, 11);
397+
ASSERT_EQ(F16::digits10, 3);
398+
ASSERT_EQ(F16::max_digits10, 5);
399+
ASSERT_EQ(F16::min_exponent, -13);
400+
ASSERT_EQ(F16::max_exponent, 16);
401+
ASSERT_EQ(F16::min_exponent10, -4);
402+
ASSERT_EQ(F16::max_exponent10, 4);
403+
404+
// Special values
405+
ASSERT_FLOAT_EQ(F16::max().ToFloat(), 65504.0f); // Largest finite value
406+
ASSERT_EQ(F16::lowest(), -F16::max()); // Most negative = -max
407+
ASSERT_FLOAT_EQ(F16::epsilon().ToFloat(), 0.0009765625f); // 2^-10
408+
ASSERT_FLOAT_EQ(F16::round_error().ToFloat(), 0.5f); // Round-to-nearest
409+
ASSERT_TRUE(F16::infinity().is_infinity());
410+
ASSERT_FALSE(F16::infinity().signbit());
411+
ASSERT_TRUE((-F16::infinity()).is_infinity());
412+
ASSERT_TRUE((-F16::infinity()).signbit());
413+
ASSERT_TRUE(F16::quiet_NaN().is_nan());
414+
ASSERT_TRUE(F16::signaling_NaN().is_nan());
415+
416+
// min() is smallest positive normal, denorm_min() is smallest subnormal
417+
ASSERT_TRUE(F16::min().is_finite());
418+
ASSERT_FALSE(F16::min().signbit());
419+
ASSERT_TRUE(F16::denorm_min().is_finite());
420+
ASSERT_FALSE(F16::denorm_min().signbit());
421+
422+
// Verify special values semantics
423+
ASSERT_TRUE(F16::infinity().is_infinity());
424+
ASSERT_TRUE((-F16::infinity()).is_infinity());
425+
ASSERT_TRUE(F16::min() > Float16::FromBits(0));
426+
ASSERT_TRUE(F16::denorm_min() > Float16::FromBits(0));
427+
ASSERT_TRUE(F16::denorm_min() < F16::min());
428+
429+
// Verify epsilon: 1 + epsilon != 1
430+
auto one = Float16(1.0f);
431+
auto one_plus_epsilon = Float16(one.ToFloat() + F16::epsilon().ToFloat());
432+
ASSERT_NE(one, one_plus_epsilon);
433+
434+
// Verify round_error is 0.5
435+
ASSERT_FLOAT_EQ(F16::round_error().ToFloat(), 0.5f);
436+
ASSERT_FLOAT_EQ(F32::round_error(), 0.5f);
437+
}
438+
375439
} // namespace
376440
} // namespace arrow::util

0 commit comments

Comments
 (0)