Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

16-bit float support #296

Merged
merged 20 commits into from
Feb 7, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
make it build
lemire authored and dalle committed Dec 3, 2024
commit 6f8fd6728dad06a8987b1f8daf843daab9071af9
2 changes: 1 addition & 1 deletion include/fast_float/digit_comparison.h
Original file line number Diff line number Diff line change
@@ -72,7 +72,7 @@ to_extended(T value) noexcept {
binary_format<T>::minimum_exponent();
equiv_uint bits;
#if FASTFLOAT_HAS_BIT_CAST
bits = std::bit_cast<equiv_uint>(value);
bits = bit_cast<equiv_uint>(value);
#else
::memcpy(&bits, &value, sizeof(T));
#endif
210 changes: 203 additions & 7 deletions include/fast_float/float_common.h
Original file line number Diff line number Diff line change
@@ -260,6 +260,88 @@ struct is_supported_char_type
> {
};

union float_union {
float f;
uint32_t bits;
};

union double_union {
double f;
uint64_t bits;
};

template <typename T, typename U> constexpr T bit_cast(const U &u);

template <>
fastfloat_really_inline constexpr float bit_cast(const uint32_t &u) {
float_union fu;
fu.bits = u;
return fu.f;
}

template <>
fastfloat_really_inline constexpr double bit_cast(const uint64_t &u) {
double_union fu;
fu.bits = u;
return fu.f;
}

template <>
fastfloat_really_inline constexpr uint32_t bit_cast(const float &u) {
float_union fu;
fu.f = u;
return fu.bits;
}

template <>
fastfloat_really_inline constexpr uint64_t bit_cast(const double &u) {
double_union fu;
fu.f = u;
return fu.bits;
}

#ifdef __STDCPP_FLOAT16_T__
union float16_union {
std::float16_t f;
uint16_t bits;
};

template <>
fastfloat_really_inline constexpr uint16_t bit_cast(const std::float16_t &u) {
float16_union fu;
fu.f = u;
return fu.bits;
}

template <>
fastfloat_really_inline constexpr std::float16_t bit_cast(const uint16_t &u) {
float16_union fu;
fu.bits = u;
return fu.f;
}
#endif // __STDCPP_FLOAT16_T__

#ifdef __STDCPP_BFLOAT16_T__
union bfloat16_union {
std::bfloat16_t f;
uint16_t bits;
};

template <>
fastfloat_really_inline constexpr uint16_t bit_cast(const std::bfloat16_t &u) {
bfloat16_union fu;
fu.f = u;
return fu.bits;
}

template <>
fastfloat_really_inline constexpr std::bfloat16_t bit_cast(const uint16_t &u) {
bfloat16_union fu;
fu.bits = u;
return fu.f;
}
#endif // __STDCPP_BFLOAT16_T__

// Compares two ASCII strings in a case insensitive manner.
template <typename UC>
inline FASTFLOAT_CONSTEXPR14 bool
@@ -630,12 +712,13 @@ inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {

// credit: Jakub Jelínek
#ifdef __STDCPP_FLOAT16_T__

template <typename U> struct binary_format_lookup_tables<std::float16_t, U> {
static constexpr std::float16_t powers_of_ten[] = {};
static constexpr uint64_t max_mantissa[] = {};
static constexpr std::float16_t powers_of_ten[] = {1}; // todo: fix this
static constexpr uint64_t max_mantissa[] = {1}; // todo: fix this
};

#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE

template <typename U>
constexpr std::float16_t
binary_format_lookup_tables<std::float16_t, U>::powers_of_ten[];
@@ -644,6 +727,33 @@ template <typename U>
constexpr uint64_t
binary_format_lookup_tables<std::float16_t, U>::max_mantissa[];

#endif

template <>
inline constexpr std::float16_t
binary_format<std::float16_t>::exact_power_of_ten(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power];
}

template <>
inline constexpr binary_format<std::float16_t>::equiv_uint
binary_format<std::float16_t>::exponent_mask() {
return 0x7C00;
}

template <>
inline constexpr binary_format<std::float16_t>::equiv_uint
binary_format<std::float16_t>::mantissa_mask() {
return 0x03FF;
}

template <>
inline constexpr binary_format<std::float16_t>::equiv_uint
binary_format<std::float16_t>::hidden_bit_mask() {
return 0x0400;
}

template <>
inline constexpr int binary_format<std::float16_t>::max_exponent_fast_path() {
return 0;
@@ -655,6 +765,13 @@ binary_format<std::float16_t>::max_mantissa_fast_path() {
return 0;
}

template <>
inline constexpr uint64_t
binary_format<std::float16_t>::max_mantissa_fast_path(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power];
}

template <>
inline constexpr int binary_format<std::float16_t>::min_exponent_fast_path() {
return 0;
@@ -705,13 +822,13 @@ template <> constexpr size_t binary_format<std::float16_t>::max_digits() {

// credit: Jakub Jelínek
#ifdef __STDCPP_BFLOAT16_T__

template <typename U> struct binary_format_lookup_tables<std::bfloat16_t, U> {
static constexpr std::bfloat16_t powers_of_ten[] = {};

static constexpr uint64_t max_mantissa[] = {};
static constexpr std::bfloat16_t powers_of_ten[] = {1}; // todo: fix this
static constexpr uint64_t max_mantissa[] = {1}; // todo: fix this
};

#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE

template <typename U>
constexpr std::bfloat16_t
binary_format_lookup_tables<std::bfloat16_t, U>::powers_of_ten[];
@@ -720,17 +837,51 @@ template <typename U>
constexpr uint64_t
binary_format_lookup_tables<std::bfloat16_t, U>::max_mantissa[];

#endif

template <>
inline constexpr std::bfloat16_t
binary_format<std::bfloat16_t>::exact_power_of_ten(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power];
}

template <>
inline constexpr int binary_format<std::bfloat16_t>::max_exponent_fast_path() {
return 0;
}

template <>
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
binary_format<std::bfloat16_t>::exponent_mask() {
return 0x7F80;
}

template <>
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
binary_format<std::bfloat16_t>::mantissa_mask() {
return 0x007F;
}

template <>
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
binary_format<std::bfloat16_t>::hidden_bit_mask() {
return 0x0080;
}

template <>
inline constexpr uint64_t
binary_format<std::bfloat16_t>::max_mantissa_fast_path() {
return 0;
}

template <>
inline constexpr uint64_t
binary_format<std::bfloat16_t>::max_mantissa_fast_path(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power];
}

template <>
inline constexpr int binary_format<std::bfloat16_t>::min_exponent_fast_path() {
return 0;
@@ -892,6 +1043,34 @@ to_float(bool negative, adjusted_mantissa am, T &value) {
#endif
}

#ifdef __STDCPP_FLOAT16_T__
template <>
fastfloat_really_inline void to_float<std::float16_t>(bool negative,
adjusted_mantissa am,
std::float16_t &value) {
constexpr int mantissa_bits =
binary_format<std::float16_t>::mantissa_explicit_bits();
value = bit_cast<std::float16_t>(
uint16_t(am.mantissa | (uint16_t(am.power2) << mantissa_bits) |
(negative ? 0x8000 : 0)));
}

#endif // __STDCPP_FLOAT16_T__

#ifdef __STDCPP_BFLOAT16_T__
template <>
fastfloat_really_inline void to_float<std::bfloat16_t>(bool negative,
adjusted_mantissa am,
std::bfloat16_t &value) {
constexpr int mantissa_bits =
binary_format<std::bfloat16_t>::mantissa_explicit_bits();
value = bit_cast<std::bfloat16_t>(
uint16_t(am.mantissa | (uint16_t(am.power2) << mantissa_bits) |
(negative ? 0x8000 : 0)));
}

#endif // __STDCPP_BFLOAT16_T__

template <typename = void> struct space_lut {
static constexpr bool value[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -944,10 +1123,12 @@ template <> constexpr char16_t const *str_const_nan<char16_t>() {
template <> constexpr char32_t const *str_const_nan<char32_t>() {
return U"nan";
}

#ifdef __cpp_char8_t
template <> constexpr char8_t const *str_const_nan<char8_t>() {
return u8"nan";
}

#endif

template <typename UC> constexpr UC const *str_const_inf();
@@ -965,10 +1146,12 @@ template <> constexpr char16_t const *str_const_inf<char16_t>() {
template <> constexpr char32_t const *str_const_inf<char32_t>() {
return U"infinity";
}

#ifdef __cpp_char8_t
template <> constexpr char8_t const *str_const_inf<char8_t>() {
return u8"infinity";
}

#endif

template <typename = void> struct int_luts {
@@ -1038,6 +1221,7 @@ fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) {

static_assert(std::is_same<equiv_uint_t<double>, uint64_t>::value,
"equiv_uint should be uint64_t for double");

static_assert(std::is_same<equiv_uint_t<float>, uint32_t>::value,
"equiv_uint should be uint32_t for float");

@@ -1051,6 +1235,18 @@ static_assert(std::is_same<equiv_uint_t<std::float32_t>, uint32_t>::value,
"equiv_uint should be uint32_t for std::float32_t");
#endif

#ifdef __STDCPP_FLOAT16_T__
static_assert(
std::is_same<binary_format<std::float16_t>::equiv_uint, uint16_t>::value,
"equiv_uint should be uint16_t for std::float16_t");
#endif

#ifdef __STDCPP_BFLOAT16_T__
static_assert(
std::is_same<binary_format<std::bfloat16_t>::equiv_uint, uint16_t>::value,
"equiv_uint should be uint16_t for std::bfloat16_t");
#endif

constexpr chars_format operator~(chars_format rhs) noexcept {
using int_type = std::underlying_type<chars_format>::type;
return static_cast<chars_format>(~static_cast<int_type>(rhs));
1 change: 1 addition & 0 deletions tests/example_test.cpp
Original file line number Diff line number Diff line change
@@ -122,6 +122,7 @@ int main() {
}
std::cout << "parsed the number " << result << std::endl;
#ifdef __STDCPP_FLOAT16_T__
printf("16-bit float\n");
// Parse as 16-bit float
std::float16_t parsed_16{};
input = "10000e-1452";