1
+ #include " assertions/check.h"
1
2
// Copyright 2022 Google LLC
2
3
//
3
4
// Licensed under the Apache License, Version 2.0 (the "License");
25
26
#include < intrin.h>
26
27
#endif
27
28
29
+ #include " assertions/builtin.h"
30
+ #include " assertions/unreachable.h"
28
31
#include " macros/always_inline.h"
29
32
#include " marker/unsafe.h"
33
+ #include " num/fp_category.h"
30
34
31
35
namespace sus ::num::__private {
32
36
@@ -1121,30 +1125,44 @@ sus_always_inline constexpr T nan() noexcept {
1121
1125
template <class T >
1122
1126
requires (std::is_floating_point_v<T>)
1123
1127
sus_always_inline constexpr T infinity () noexcept {
1128
+ // SAFETY: The value being constructed is non a NaN so we can do this in a
1129
+ // constexpr way.
1124
1130
if constexpr (sizeof (T) == sizeof (float ))
1125
- return into_float ( uint32_t {0x7f800000 });
1131
+ return into_float_constexpr (unsafe_fn, uint32_t {0x7f800000 });
1126
1132
else
1127
- return into_float ( uint64_t {0x7ff0000000000000 });
1133
+ return into_float_constexpr (unsafe_fn, uint64_t {0x7ff0000000000000 });
1128
1134
}
1129
1135
1130
1136
template <class T >
1131
1137
requires (std::is_floating_point_v<T>)
1132
1138
sus_always_inline constexpr T negative_infinity () noexcept {
1139
+ // SAFETY: The value being constructed is non a NaN so we can do this in a
1140
+ // constexpr way.
1133
1141
if constexpr (sizeof (T) == sizeof (float ))
1134
- return into_float ( uint32_t {0xff800000 });
1142
+ return into_float_constexpr (unsafe_fn, uint32_t {0xff800000 });
1135
1143
else
1136
- return into_float ( uint64_t {0xfff0000000000000 });
1144
+ return into_float_constexpr (unsafe_fn, uint64_t {0xfff0000000000000 });
1137
1145
}
1138
1146
1139
- constexpr int32_t exponent (float x) noexcept {
1147
+ constexpr int32_t exponent_bits (float x) noexcept {
1140
1148
constexpr uint32_t mask = 0b01111111100000000000000000000000 ;
1141
- return ((into_unsigned_integer (x) & mask) >> 23 ) - int32_t {127 };
1149
+ return static_cast <int32_t >(
1150
+ unchecked_shr (into_unsigned_integer (x) & mask, 23 ));
1142
1151
}
1143
1152
1144
- constexpr int32_t exponent (double x) noexcept {
1153
+ constexpr int32_t exponent_bits (double x) noexcept {
1145
1154
constexpr uint64_t mask =
1146
1155
0b0111111111110000000000000000000000000000000000000000000000000000 ;
1147
- return ((into_unsigned_integer (x) & mask) >> 52 ) - int32_t {1023 };
1156
+ return static_cast <int32_t >(
1157
+ unchecked_shr (into_unsigned_integer (x) & mask, 52 ));
1158
+ }
1159
+
1160
+ constexpr int32_t exponent_value (float x) noexcept {
1161
+ return exponent_bits (x) - int32_t {127 };
1162
+ }
1163
+
1164
+ constexpr int32_t exponent_value (double x) noexcept {
1165
+ return exponent_bits (x) - int32_t {1023 };
1148
1166
}
1149
1167
1150
1168
constexpr uint32_t mantissa (float x) noexcept {
@@ -1175,15 +1193,23 @@ constexpr bool float_is_inf_or_nan(double x) noexcept {
1175
1193
}
1176
1194
1177
1195
constexpr bool float_is_nan (float x) noexcept {
1196
+ #if __has_builtin(__builtin_isnan)
1197
+ return __builtin_isnan (x);
1198
+ #else
1178
1199
constexpr auto inf_mask = uint32_t {0x7f800000 };
1179
1200
constexpr auto nan_mask = uint32_t {0x7fffffff };
1180
1201
return (into_unsigned_integer (x) & nan_mask) > inf_mask;
1202
+ #endif
1181
1203
}
1182
1204
1183
1205
constexpr bool float_is_nan (double x) noexcept {
1206
+ #if __has_builtin(__builtin_isnan)
1207
+ return __builtin_isnan (x);
1208
+ #else
1184
1209
constexpr auto inf_mask = uint64_t {0x7ff0000000000000 };
1185
1210
constexpr auto nan_mask = uint64_t {0x7fffffffffffffff };
1186
1211
return (into_unsigned_integer (x) & nan_mask) > inf_mask;
1212
+ #endif
1187
1213
}
1188
1214
1189
1215
// Assumes that x is a NaN.
@@ -1200,6 +1226,12 @@ constexpr bool float_is_nan_quiet(double x) noexcept {
1200
1226
return (into_unsigned_integer (x) & quiet_mask) != 0 ;
1201
1227
}
1202
1228
1229
+ template <class T >
1230
+ requires (std::is_floating_point_v<T> && sizeof (T) <= 8 )
1231
+ constexpr bool float_is_subnormal (T x) noexcept {
1232
+ return exponent_bits (x) == int32_t {0 };
1233
+ }
1234
+
1203
1235
template <class T >
1204
1236
requires (std::is_floating_point_v<T> && sizeof (T) <= 8 )
1205
1237
constexpr T truncate_float (T x) noexcept {
@@ -1208,21 +1240,21 @@ constexpr T truncate_float(T x) noexcept {
1208
1240
1209
1241
if (float_is_inf_or_nan (x) || float_is_zero (x)) return x;
1210
1242
1211
- const int32_t exp = exponent (x);
1243
+ const int32_t exponent = exponent_value (x);
1212
1244
1213
1245
// If the exponent is greater than the most negative mantissa
1214
1246
// exponent, then x is already an integer.
1215
- if (exp >= static_cast <int32_t >(mantissa_width)) return x;
1247
+ if (exponent >= static_cast <int32_t >(mantissa_width)) return x;
1216
1248
1217
1249
// If the exponent is such that abs(x) is less than 1, then return 0.
1218
- if (exp <= -1 ) {
1250
+ if (exponent <= -1 ) {
1219
1251
if ((into_unsigned_integer (x) & high_bit<T>()) != 0 )
1220
1252
return T{-0.0 };
1221
1253
else
1222
1254
return T{0.0 };
1223
1255
}
1224
1256
1225
- const uint32_t trim_bits = mantissa_width - static_cast <uint32_t >(exp );
1257
+ const uint32_t trim_bits = mantissa_width - static_cast <uint32_t >(exponent );
1226
1258
const auto shr = unchecked_shr (into_unsigned_integer (x), trim_bits);
1227
1259
const auto shl = unchecked_shl (shr, trim_bits);
1228
1260
// SAFETY: The value here is not a NaN, so will give the same value in
@@ -1252,4 +1284,46 @@ inline T float_round(T x) noexcept {
1252
1284
(into_unsigned_integer (x) & high_bit<T>()));
1253
1285
}
1254
1286
1287
+ #if __has_builtin(__builtin_fpclassify)
1288
+ template <class T >
1289
+ requires (std::is_floating_point_v<T> && sizeof (T) <= 8)
1290
+ constexpr inline ::sus::num::FpCategory float_category(T x) noexcept {
1291
+ constexpr auto nan = 1 ;
1292
+ constexpr auto inf = 2 ;
1293
+ constexpr auto norm = 3 ;
1294
+ constexpr auto subnorm = 4 ;
1295
+ constexpr auto zero = 5 ;
1296
+ switch (__builtin_fpclassify (nan, inf, norm, subnorm, zero, x)) {
1297
+ case nan: return ::sus::num::FpCategory::Nan;
1298
+ case inf: return ::sus::num::FpCategory::Infinite;
1299
+ case norm: return ::sus::num::FpCategory::Normal;
1300
+ case subnorm: return ::sus::num::FpCategory::Subnormal;
1301
+ case zero: return ::sus::num::FpCategory::Zero;
1302
+ default : ::sus::unreachable_unchecked (unsafe_fn);
1303
+ }
1304
+ }
1305
+ #else
1306
+ template <class T >
1307
+ requires (std::is_floating_point_v<T> && sizeof (T) <= 8)
1308
+ constexpr inline ::sus::num::FpCategory float_category(T x) noexcept {
1309
+ if (std::is_constant_evaluated ()) {
1310
+ if (float_is_nan (x)) return ::sus::num::FpCategory::Nan;
1311
+ if (float_is_inf_or_nan (x)) return ::sus::num::FpCategory::Infinite;
1312
+ if (float_is_zero (x)) return ::sus::num::FpCategory::Zero;
1313
+ if (float_is_subnormal (x)) return ::sus::num::FpCategory::Subnormal;
1314
+ return ::sus::num::FpCategory::Normal;
1315
+ } else {
1316
+ // C++23 requires a constexpr way to do this.
1317
+ switch (::fpclassify (x)) {
1318
+ case FP_NAN: return ::sus::num::FpCategory::Nan;
1319
+ case FP_INFINITE: return ::sus::num::FpCategory::Infinite;
1320
+ case FP_NORMAL: return ::sus::num::FpCategory::Normal;
1321
+ case FP_SUBNORMAL: return ::sus::num::FpCategory::Subnormal;
1322
+ case FP_ZERO: return ::sus::num::FpCategory::Zero;
1323
+ default : ::sus::unreachable_unchecked (unsafe_fn);
1324
+ }
1325
+ }
1326
+ }
1327
+ #endif
1328
+
1255
1329
} // namespace sus::num::__private
0 commit comments