Skip to content

Commit a6a67bb

Browse files
committed
Add boolean is nan/normal/zero etc functions for floats
1 parent 81578cd commit a6a67bb

File tree

4 files changed

+285
-14
lines changed

4 files changed

+285
-14
lines changed

num/__private/float_macros.h

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -576,20 +576,63 @@
576576
} \
577577
static_assert(true)
578578

579-
#define _sus__float_category(T) \
580-
/** Returns the floating point category of the number. \
581-
* \
582-
* If only one property is going to be tested, it is generally faster to use \
583-
* the specific predicate instead. \
584-
*/ \
585-
constexpr inline FpCategory classify() const& noexcept { \
586-
return __private::float_category(primitive_value); \
587-
} \
579+
#define _sus__float_category(T) \
580+
/** Returns the floating point category of the number. \
581+
* \
582+
* If only one property is going to be tested, it is generally faster to use \
583+
* the specific predicate instead. \
584+
*/ \
585+
constexpr inline FpCategory classify() const& noexcept { \
586+
return __private::float_category(primitive_value); \
587+
} \
588+
/** Returns true if this number is neither infinite nor NaN. \
589+
*/ \
590+
constexpr inline bool is_finite() const& noexcept { \
591+
return !__private::float_is_inf_or_nan(primitive_value); \
592+
} \
593+
/** Returns true if this value is positive infinity or negative infinity, \
594+
* and false otherwise. \
595+
*/ \
596+
constexpr inline bool is_infinite() const& noexcept { \
597+
return __private::float_is_inf(primitive_value); \
598+
} \
599+
/** Returns true if this value is NaN. \
600+
*/ \
601+
constexpr inline bool is_nan() const& noexcept { \
602+
return __private::float_is_nan(primitive_value); \
603+
} \
604+
/** Returns true if the number is neither zero, infinite, subnormal, or NaN. \
605+
*/ \
606+
constexpr inline bool is_normal() const& noexcept { \
607+
return __private::float_is_normal(primitive_value); \
608+
} \
609+
/** Returns true if self has a negative sign, including -0.0, NaNs with \
610+
* negative sign bit and negative infinity. \
611+
* \
612+
* Note that IEEE-745 doesn’t assign any meaning to the sign bit in case of \
613+
* a NaN \
614+
*/ \
615+
constexpr inline bool is_sign_negative() const& noexcept { \
616+
return __private::float_signbit(primitive_value); \
617+
} \
618+
/** Returns true if self has a positive sign, including +0.0, NaNs with \
619+
* positive sign bit and positive infinity. \
620+
* \
621+
* Note that IEEE-745 doesn’t assign any meaning to the sign bit in case of \
622+
* a NaN. \
623+
*/ \
624+
constexpr inline bool is_sign_positive() const& noexcept { \
625+
return !__private::float_signbit(primitive_value); \
626+
} \
627+
/** Returns true if the number is subnormal. \
628+
*/ \
629+
constexpr inline bool is_subnormal() const& noexcept { \
630+
return !__private::float_is_zero(primitive_value) && \
631+
__private::float_nonzero_is_subnormal(primitive_value); \
632+
} \
588633
static_assert(true)
589634

590635
// clamp
591-
// classify, is_finite, is_infinite, is_nan, is_normal, is_sign_negative,
592-
// is_sign_positive, is_subnormal
593636
// div_euclid, rem_euclid
594637
// from_be_bytes, from_le_bytes, from_ne_bytes
595638
// to_be_bytes, to_le_bytes, to_ne_bytes

num/__private/intrinsics.h

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,27 @@ constexpr bool float_is_zero(T x) noexcept {
11821182
return (into_unsigned_integer(x) & ~high_bit<T>()) == 0;
11831183
}
11841184

1185+
constexpr bool float_is_inf(float x) noexcept {
1186+
#if __has_builtin(__builtin_isinf)
1187+
return __builtin_isinf(x);
1188+
#else
1189+
constexpr auto inf = uint32_t{0x7f800000};
1190+
constexpr auto mask = uint32_t{0x7fffffff};
1191+
const auto y = into_unsigned_integer(x);
1192+
return (y & mask) == inf;
1193+
#endif
1194+
}
1195+
1196+
constexpr bool float_is_inf(double x) noexcept {
1197+
#if __has_builtin(__builtin_isinf)
1198+
return __builtin_isinf(x);
1199+
#else
1200+
constexpr auto inf = uint64_t{0x7ff0000000000000};
1201+
constexpr auto mask = uint64_t{0x7fffffffffffffff};
1202+
return (into_unsigned_integer(x) & mask) == inf;
1203+
#endif
1204+
}
1205+
11851206
constexpr bool float_is_inf_or_nan(float x) noexcept {
11861207
constexpr auto mask = uint32_t{0x7f800000};
11871208
return (into_unsigned_integer(x) & mask) == mask;
@@ -1226,12 +1247,27 @@ constexpr bool float_is_nan_quiet(double x) noexcept {
12261247
return (into_unsigned_integer(x) & quiet_mask) != 0;
12271248
}
12281249

1250+
// This is only valid if the argument is not (positive or negative) zero.
12291251
template <class T>
12301252
requires(std::is_floating_point_v<T> && sizeof(T) <= 8)
1231-
constexpr bool float_is_subnormal(T x) noexcept {
1253+
constexpr bool float_nonzero_is_subnormal(T x) noexcept {
12321254
return exponent_bits(x) == int32_t{0};
12331255
}
12341256

1257+
constexpr bool float_is_normal(float x) noexcept {
1258+
// If the exponent is 0, the number is zero or subnormal. If the exponent is
1259+
// all ones, the number is infinite or NaN.
1260+
const auto e = exponent_bits(x);
1261+
return e != 0 && e != int32_t{0b011111111};
1262+
}
1263+
1264+
constexpr bool float_is_normal(double x) noexcept {
1265+
// If the exponent is 0, the number is zero or subnormal. If the exponent is
1266+
// all ones, the number is infinite or NaN.
1267+
const auto e = exponent_bits(x);
1268+
return e != 0 && e != int32_t{0b011111111111};
1269+
}
1270+
12351271
template <class T>
12361272
requires(std::is_floating_point_v<T> && sizeof(T) <= 8)
12371273
constexpr T truncate_float(T x) noexcept {
@@ -1262,6 +1298,12 @@ constexpr T truncate_float(T x) noexcept {
12621298
return into_float_constexpr(unsafe_fn, shl);
12631299
}
12641300

1301+
template <class T>
1302+
requires(std::is_floating_point_v<T> && sizeof(T) <= 8)
1303+
constexpr bool float_signbit(T x) noexcept {
1304+
return unchecked_and(into_unsigned_integer(x), high_bit<T>()) != 0;
1305+
}
1306+
12651307
template <class T>
12661308
requires(std::is_floating_point_v<T> && sizeof(T) <= 8)
12671309
constexpr T float_signum(T x) noexcept {
@@ -1270,7 +1312,9 @@ constexpr T float_signum(T x) noexcept {
12701312
if (float_is_nan(x)) [[unlikely]]
12711313
return x;
12721314
const auto signbit = unchecked_and(into_unsigned_integer(x), high_bit<T>());
1273-
return into_float(unchecked_add(into_unsigned_integer(T{1}), signbit));
1315+
// SAFETY: The value passed in is constructed here and is not a NaN.
1316+
return into_float_constexpr(
1317+
unsafe_fn, unchecked_add(into_unsigned_integer(T{1}), signbit));
12741318
}
12751319

12761320
template <class T>
@@ -1310,7 +1354,7 @@ constexpr inline ::sus::num::FpCategory float_category(T x) noexcept {
13101354
if (float_is_nan(x)) return ::sus::num::FpCategory::Nan;
13111355
if (float_is_inf_or_nan(x)) return ::sus::num::FpCategory::Infinite;
13121356
if (float_is_zero(x)) return ::sus::num::FpCategory::Zero;
1313-
if (float_is_subnormal(x)) return ::sus::num::FpCategory::Subnormal;
1357+
if (float_nonzero_is_subnormal(x)) return ::sus::num::FpCategory::Subnormal;
13141358
return ::sus::num::FpCategory::Normal;
13151359
} else {
13161360
// C++23 requires a constexpr way to do this.

num/f32_unittest.cc

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,4 +851,96 @@ TEST(f32, Classify) {
851851
EXPECT_EQ(g, FpCategory::Normal);
852852
}
853853

854+
TEST(f32, IsFinite) {
855+
EXPECT_FALSE(f32::TODO_INFINITY().is_finite());
856+
EXPECT_FALSE(f32::NEG_INFINITY().is_finite());
857+
EXPECT_FALSE(f32::TODO_NAN().is_finite());
858+
EXPECT_TRUE((0_f32).is_finite());
859+
EXPECT_TRUE((-0_f32).is_finite());
860+
EXPECT_TRUE(
861+
f32(std::numeric_limits<decltype(f32::primitive_value)>::denorm_min())
862+
.is_finite());
863+
EXPECT_TRUE((123_f32).is_finite());
864+
}
865+
866+
TEST(f32, IsInfinite) {
867+
EXPECT_TRUE(f32::TODO_INFINITY().is_infinite());
868+
EXPECT_TRUE(f32::NEG_INFINITY().is_infinite());
869+
EXPECT_FALSE(f32::TODO_NAN().is_infinite());
870+
EXPECT_FALSE((0_f32).is_infinite());
871+
EXPECT_FALSE((-0_f32).is_infinite());
872+
EXPECT_FALSE(
873+
f32(std::numeric_limits<decltype(f32::primitive_value)>::denorm_min())
874+
.is_infinite());
875+
EXPECT_FALSE((123_f32).is_infinite());
876+
}
877+
878+
TEST(f32, IsNan) {
879+
EXPECT_FALSE(f32::TODO_INFINITY().is_nan());
880+
EXPECT_FALSE(f32::NEG_INFINITY().is_nan());
881+
EXPECT_TRUE(f32::TODO_NAN().is_nan());
882+
EXPECT_FALSE((0_f32).is_nan());
883+
EXPECT_FALSE((-0_f32).is_nan());
884+
EXPECT_FALSE(
885+
f32(std::numeric_limits<decltype(f32::primitive_value)>::denorm_min())
886+
.is_nan());
887+
EXPECT_FALSE((123_f32).is_nan());
888+
}
889+
890+
TEST(f32, IsNormal) {
891+
EXPECT_FALSE(f32::TODO_INFINITY().is_normal());
892+
EXPECT_FALSE(f32::NEG_INFINITY().is_normal());
893+
EXPECT_FALSE(f32::TODO_NAN().is_normal());
894+
EXPECT_FALSE((0_f32).is_normal());
895+
EXPECT_FALSE((-0_f32).is_normal());
896+
EXPECT_FALSE(
897+
f32(std::numeric_limits<decltype(f32::primitive_value)>::denorm_min())
898+
.is_normal());
899+
EXPECT_TRUE((123_f32).is_normal());
900+
}
901+
902+
TEST(f32, IsSignNegative) {
903+
EXPECT_FALSE(f32::TODO_INFINITY().is_sign_negative());
904+
EXPECT_TRUE(f32::NEG_INFINITY().is_sign_negative());
905+
EXPECT_FALSE(f32::TODO_NAN().is_sign_negative());
906+
EXPECT_FALSE((0_f32).is_sign_negative());
907+
EXPECT_TRUE((-0_f32).is_sign_negative());
908+
EXPECT_FALSE(
909+
f32(std::numeric_limits<decltype(f32::primitive_value)>::denorm_min())
910+
.is_sign_negative());
911+
EXPECT_TRUE(
912+
f32(-std::numeric_limits<decltype(f32::primitive_value)>::denorm_min())
913+
.is_sign_negative());
914+
EXPECT_FALSE((123_f32).is_sign_negative());
915+
EXPECT_TRUE((-123_f32).is_sign_negative());
916+
}
917+
918+
TEST(f32, IsSignPositive) {
919+
EXPECT_TRUE(f32::TODO_INFINITY().is_sign_positive());
920+
EXPECT_FALSE(f32::NEG_INFINITY().is_sign_positive());
921+
EXPECT_TRUE(f32::TODO_NAN().is_sign_positive());
922+
EXPECT_TRUE((0_f32).is_sign_positive());
923+
EXPECT_FALSE((-0_f32).is_sign_positive());
924+
EXPECT_TRUE(
925+
f32(std::numeric_limits<decltype(f32::primitive_value)>::denorm_min())
926+
.is_sign_positive());
927+
EXPECT_FALSE(
928+
f32(-std::numeric_limits<decltype(f32::primitive_value)>::denorm_min())
929+
.is_sign_positive());
930+
EXPECT_TRUE((123_f32).is_sign_positive());
931+
EXPECT_FALSE((-123_f32).is_sign_positive());
932+
}
933+
934+
TEST(f32, IsSubnormal) {
935+
EXPECT_FALSE(f32::TODO_INFINITY().is_subnormal());
936+
EXPECT_FALSE(f32::NEG_INFINITY().is_subnormal());
937+
EXPECT_FALSE(f32::TODO_NAN().is_subnormal());
938+
EXPECT_FALSE((0_f32).is_subnormal());
939+
EXPECT_FALSE((-0_f32).is_subnormal());
940+
EXPECT_TRUE(
941+
f32(std::numeric_limits<decltype(f32::primitive_value)>::denorm_min())
942+
.is_subnormal());
943+
EXPECT_FALSE((123_f32).is_subnormal());
944+
}
945+
854946
} // namespace

num/f64_unittest.cc

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,4 +840,96 @@ TEST(f64, Classify) {
840840
EXPECT_EQ(g, FpCategory::Normal);
841841
}
842842

843+
TEST(f64, IsFinite) {
844+
EXPECT_FALSE(f64::TODO_INFINITY().is_finite());
845+
EXPECT_FALSE(f64::NEG_INFINITY().is_finite());
846+
EXPECT_FALSE(f64::TODO_NAN().is_finite());
847+
EXPECT_TRUE((0_f64).is_finite());
848+
EXPECT_TRUE((-0_f64).is_finite());
849+
EXPECT_TRUE(
850+
f64(std::numeric_limits<decltype(f64::primitive_value)>::denorm_min())
851+
.is_finite());
852+
EXPECT_TRUE((123_f64).is_finite());
853+
}
854+
855+
TEST(f64, IsInfinite) {
856+
EXPECT_TRUE(f64::TODO_INFINITY().is_infinite());
857+
EXPECT_TRUE(f64::NEG_INFINITY().is_infinite());
858+
EXPECT_FALSE(f64::TODO_NAN().is_infinite());
859+
EXPECT_FALSE((0_f64).is_infinite());
860+
EXPECT_FALSE((-0_f64).is_infinite());
861+
EXPECT_FALSE(
862+
f64(std::numeric_limits<decltype(f64::primitive_value)>::denorm_min())
863+
.is_infinite());
864+
EXPECT_FALSE((123_f64).is_infinite());
865+
}
866+
867+
TEST(f64, IsNan) {
868+
EXPECT_FALSE(f64::TODO_INFINITY().is_nan());
869+
EXPECT_FALSE(f64::NEG_INFINITY().is_nan());
870+
EXPECT_TRUE(f64::TODO_NAN().is_nan());
871+
EXPECT_FALSE((0_f64).is_nan());
872+
EXPECT_FALSE((-0_f64).is_nan());
873+
EXPECT_FALSE(
874+
f64(std::numeric_limits<decltype(f64::primitive_value)>::denorm_min())
875+
.is_nan());
876+
EXPECT_FALSE((123_f64).is_nan());
877+
}
878+
879+
TEST(f64, IsNormal) {
880+
EXPECT_FALSE(f64::TODO_INFINITY().is_normal());
881+
EXPECT_FALSE(f64::NEG_INFINITY().is_normal());
882+
EXPECT_FALSE(f64::TODO_NAN().is_normal());
883+
EXPECT_FALSE((0_f64).is_normal());
884+
EXPECT_FALSE((-0_f64).is_normal());
885+
EXPECT_FALSE(
886+
f64(std::numeric_limits<decltype(f64::primitive_value)>::denorm_min())
887+
.is_normal());
888+
EXPECT_TRUE((123_f64).is_normal());
889+
}
890+
891+
TEST(f64, IsSignNegative) {
892+
EXPECT_FALSE(f64::TODO_INFINITY().is_sign_negative());
893+
EXPECT_TRUE(f64::NEG_INFINITY().is_sign_negative());
894+
EXPECT_FALSE(f64::TODO_NAN().is_sign_negative());
895+
EXPECT_FALSE((0_f64).is_sign_negative());
896+
EXPECT_TRUE((-0_f64).is_sign_negative());
897+
EXPECT_FALSE(
898+
f64(std::numeric_limits<decltype(f64::primitive_value)>::denorm_min())
899+
.is_sign_negative());
900+
EXPECT_TRUE(
901+
f64(-std::numeric_limits<decltype(f64::primitive_value)>::denorm_min())
902+
.is_sign_negative());
903+
EXPECT_FALSE((123_f64).is_sign_negative());
904+
EXPECT_TRUE((-123_f64).is_sign_negative());
905+
}
906+
907+
TEST(f64, IsSignPositive) {
908+
EXPECT_TRUE(f64::TODO_INFINITY().is_sign_positive());
909+
EXPECT_FALSE(f64::NEG_INFINITY().is_sign_positive());
910+
EXPECT_TRUE(f64::TODO_NAN().is_sign_positive());
911+
EXPECT_TRUE((0_f64).is_sign_positive());
912+
EXPECT_FALSE((-0_f64).is_sign_positive());
913+
EXPECT_TRUE(
914+
f64(std::numeric_limits<decltype(f64::primitive_value)>::denorm_min())
915+
.is_sign_positive());
916+
EXPECT_FALSE(
917+
f64(-std::numeric_limits<decltype(f64::primitive_value)>::denorm_min())
918+
.is_sign_positive());
919+
EXPECT_TRUE((123_f64).is_sign_positive());
920+
EXPECT_FALSE((-123_f64).is_sign_positive());
921+
}
922+
923+
TEST(f64, IsSubnormal) {
924+
EXPECT_FALSE(f64::TODO_INFINITY().is_subnormal());
925+
EXPECT_FALSE(f64::NEG_INFINITY().is_subnormal());
926+
EXPECT_FALSE(f64::TODO_NAN().is_subnormal());
927+
EXPECT_FALSE((0_f64).is_subnormal());
928+
EXPECT_FALSE((-0_f64).is_subnormal());
929+
EXPECT_TRUE(
930+
f64(std::numeric_limits<decltype(f64::primitive_value)>::denorm_min())
931+
.is_subnormal());
932+
EXPECT_FALSE((123_f64).is_subnormal());
933+
}
934+
843935
} // namespace

0 commit comments

Comments
 (0)