#ifndef LIBSPIRV_UTIL_HEX_FLOAT_H_
#define LIBSPIRV_UTIL_HEX_FLOAT_H_
#include <cassert>
#include <cctype>
#include <cmath>
#include <cstdint>
#include <iomanip>
#include <limits>
#include <sstream>
#include "bitutils.h"
namespace spvutils {
class Float16 {
public:
Float16(uint16_t v) : val(v) {}
Float16() {}
static bool isNan(const Float16& val) {
return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) != 0);
}
static bool isInfinity(const Float16& val) {
return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) == 0);
}
Float16(const Float16& other) { val = other.val; }
uint16_t get_value() const { return val; }
static Float16 max() { return Float16(0x7bff); }
static Float16 lowest() { return Float16(0xfbff); }
private:
uint16_t val;
};
template <typename T>
struct FloatProxyTraits {
typedef void uint_type;
};
template <>
struct FloatProxyTraits<float> {
typedef uint32_t uint_type;
static bool isNan(float f) { return std::isnan(f); }
static bool isInfinity(float f) { return std::isinf(f); }
static float max() { return std::numeric_limits<float>::max(); }
static float lowest() { return std::numeric_limits<float>::lowest(); }
};
template <>
struct FloatProxyTraits<double> {
typedef uint64_t uint_type;
static bool isNan(double f) { return std::isnan(f); }
static bool isInfinity(double f) { return std::isinf(f); }
static double max() { return std::numeric_limits<double>::max(); }
static double lowest() { return std::numeric_limits<double>::lowest(); }
};
template <>
struct FloatProxyTraits<Float16> {
typedef uint16_t uint_type;
static bool isNan(Float16 f) { return Float16::isNan(f); }
static bool isInfinity(Float16 f) { return Float16::isInfinity(f); }
static Float16 max() { return Float16::max(); }
static Float16 lowest() { return Float16::lowest(); }
};
template <typename T>
class FloatProxy {
public:
typedef typename FloatProxyTraits<T>::uint_type uint_type;
FloatProxy() {}
FloatProxy(T val) { data_ = BitwiseCast<uint_type>(val); }
FloatProxy(uint_type val) { data_ = val; }
FloatProxy<T> operator-() const {
return static_cast<uint_type>(data_ ^
(uint_type(0x1) << (sizeof(T) * 8 - 1)));
}
T getAsFloat() const { return BitwiseCast<T>(data_); }
uint_type data() const { return data_; }
bool isNan() { return FloatProxyTraits<T>::isNan(getAsFloat()); }
bool isInfinity() { return FloatProxyTraits<T>::isInfinity(getAsFloat()); }
static FloatProxy<T> max() {
return FloatProxy<T>(FloatProxyTraits<T>::max());
}
static FloatProxy<T> lowest() {
return FloatProxy<T>(FloatProxyTraits<T>::lowest());
}
private:
uint_type data_;
};
template <typename T>
bool operator==(const FloatProxy<T>& first, const FloatProxy<T>& second) {
return first.data() == second.data();
}
template <typename T>
std::istream& operator>>(std::istream& is, FloatProxy<T>& value) {
T float_val;
is >> float_val;
value = FloatProxy<T>(float_val);
return is;
}
template <typename T>
struct HexFloatTraits {
typedef void uint_type;
typedef void int_type;
typedef void underlying_type;
typedef void native_type;
static const uint32_t num_used_bits = 0;
static const uint32_t num_exponent_bits = 0;
static const uint32_t num_fraction_bits = 0;
static const uint32_t exponent_bias = 0;
};
template <>
struct HexFloatTraits<FloatProxy<float>> {
typedef uint32_t uint_type;
typedef int32_t int_type;
typedef FloatProxy<float> underlying_type;
typedef float native_type;
static const uint_type num_used_bits = 32;
static const uint_type num_exponent_bits = 8;
static const uint_type num_fraction_bits = 23;
static const uint_type exponent_bias = 127;
};
template <>
struct HexFloatTraits<FloatProxy<double>> {
typedef uint64_t uint_type;
typedef int64_t int_type;
typedef FloatProxy<double> underlying_type;
typedef double native_type;
static const uint_type num_used_bits = 64;
static const uint_type num_exponent_bits = 11;
static const uint_type num_fraction_bits = 52;
static const uint_type exponent_bias = 1023;
};
template <>
struct HexFloatTraits<FloatProxy<Float16>> {
typedef uint16_t uint_type;
typedef int16_t int_type;
typedef uint16_t underlying_type;
typedef uint16_t native_type;
static const uint_type num_used_bits = 16;
static const uint_type num_exponent_bits = 5;
static const uint_type num_fraction_bits = 10;
static const uint_type exponent_bias = 15;
};
enum round_direction {
kRoundToZero,
kRoundToNearestEven,
kRoundToPositiveInfinity,
kRoundToNegativeInfinity
};
template <typename T, typename Traits = HexFloatTraits<T>>
class HexFloat {
public:
typedef typename Traits::uint_type uint_type;
typedef typename Traits::int_type int_type;
typedef typename Traits::underlying_type underlying_type;
typedef typename Traits::native_type native_type;
explicit HexFloat(T f) : value_(f) {}
T value() const { return value_; }
void set_value(T f) { value_ = f; }
static const uint32_t num_used_bits = Traits::num_used_bits;
static const uint32_t exponent_bias = Traits::exponent_bias;
static const uint32_t num_exponent_bits = Traits::num_exponent_bits;
static const uint32_t num_fraction_bits = Traits::num_fraction_bits;
static const uint32_t top_bit_left_shift = num_used_bits - 1;
static const uint32_t fraction_nibbles = (num_fraction_bits + 3) / 4;
static const uint32_t num_overflow_bits =
fraction_nibbles * 4 - num_fraction_bits;
static const uint_type fraction_represent_mask =
spvutils::SetBits<uint_type, 0,
num_fraction_bits + num_overflow_bits>::get;
static const uint_type fraction_top_bit =
uint_type(1) << (num_fraction_bits + num_overflow_bits - 1);
static const uint_type first_exponent_bit = uint_type(1)
<< (num_fraction_bits);
static const uint_type fraction_encode_mask =
spvutils::SetBits<uint_type, 0, num_fraction_bits>::get;
static const uint_type sign_mask = uint_type(1) << top_bit_left_shift;
static const uint_type exponent_mask =
spvutils::SetBits<uint_type, num_fraction_bits, num_exponent_bits>::get;
static const uint32_t exponent_left_shift = num_fraction_bits;
static const uint32_t fraction_right_shift =
static_cast<uint32_t>(sizeof(uint_type) * 8) - num_fraction_bits;
static const int_type max_exponent =
(exponent_mask >> num_fraction_bits) - exponent_bias;
static const int_type min_exponent = -static_cast<int_type>(exponent_bias);
uint_type getBits() const { return spvutils::BitwiseCast<uint_type>(value_); }
uint_type getUnsignedBits() const {
return static_cast<uint_type>(spvutils::BitwiseCast<uint_type>(value_) &
~sign_mask);
}
const uint_type getExponentBits() const {
return static_cast<uint_type>((getBits() & exponent_mask) >>
num_fraction_bits);
}
const int_type getUnbiasedExponent() const {
return static_cast<int_type>(getExponentBits() - exponent_bias);
}
const uint_type getSignificandBits() const {
return getBits() & fraction_encode_mask;
}
const int_type getUnbiasedNormalizedExponent() const {
if ((getBits() & ~sign_mask) == 0) {
return 0;
}
int_type exp = getUnbiasedExponent();
if (exp == min_exponent) {
uint_type significand_bits = getSignificandBits();
while ((significand_bits & (first_exponent_bit >> 1)) == 0) {
significand_bits = static_cast<uint_type>(significand_bits << 1);
exp = static_cast<int_type>(exp - 1);
}
significand_bits &= fraction_encode_mask;
}
return exp;
}
const uint_type getNormalizedSignificand() const {
int_type unbiased_exponent = getUnbiasedNormalizedExponent();
uint_type significand = getSignificandBits();
for (int_type i = unbiased_exponent; i <= min_exponent; ++i) {
significand = static_cast<uint_type>(significand << 1);
}
significand &= fraction_encode_mask;
return significand;
}
bool isNegative() const { return (getBits() & sign_mask) != 0; }
void setFromSignUnbiasedExponentAndNormalizedSignificand(
bool negative, int_type exponent, uint_type significand,
bool round_denorm_up) {
bool significand_is_zero = significand == 0;
if (exponent <= min_exponent) {
significand_is_zero = false;
significand |= first_exponent_bit;
significand = static_cast<uint_type>(significand >> 1);
}
while (exponent < min_exponent) {
significand = static_cast<uint_type>(significand >> 1);
++exponent;
}
if (exponent == min_exponent) {
if (significand == 0 && !significand_is_zero && round_denorm_up) {
significand = static_cast<uint_type>(0x1);
}
}
uint_type new_value = 0;
if (negative) {
new_value = static_cast<uint_type>(new_value | sign_mask);
}
exponent = static_cast<int_type>(exponent + exponent_bias);
assert(exponent >= 0);
exponent = static_cast<uint_type>((exponent << exponent_left_shift) &
exponent_mask);
significand = static_cast<uint_type>(significand & fraction_encode_mask);
new_value = static_cast<uint_type>(new_value | (exponent | significand));
value_ = BitwiseCast<T>(new_value);
}
static uint_type incrementSignificand(uint_type significand,
uint_type to_increment, bool* carry) {
significand = static_cast<uint_type>(significand + to_increment);
*carry = false;
if (significand & first_exponent_bit) {
*carry = true;
significand = static_cast<uint_type>(significand & ~first_exponent_bit);
significand = static_cast<uint_type>(significand >> 1);
}
return significand;
}
template <typename int_type>
uint_type negatable_left_shift(int_type N, uint_type val)
{
if(N >= 0)
return val << N;
return val >> -N;
}
template <typename int_type>
uint_type negatable_right_shift(int_type N, uint_type val)
{
if(N >= 0)
return val >> N;
return val << -N;
}
template <typename other_T>
typename other_T::uint_type getRoundedNormalizedSignificand(
round_direction dir, bool* carry_bit) {
typedef typename other_T::uint_type other_uint_type;
static const int_type num_throwaway_bits =
static_cast<int_type>(num_fraction_bits) -
static_cast<int_type>(other_T::num_fraction_bits);
static const uint_type last_significant_bit =
(num_throwaway_bits < 0)
? 0
: negatable_left_shift(num_throwaway_bits, 1u);
static const uint_type first_rounded_bit =
(num_throwaway_bits < 1)
? 0
: negatable_left_shift(num_throwaway_bits - 1, 1u);
static const uint_type throwaway_mask_bits =
num_throwaway_bits > 0 ? num_throwaway_bits : 0;
static const uint_type throwaway_mask =
spvutils::SetBits<uint_type, 0, throwaway_mask_bits>::get;
*carry_bit = false;
other_uint_type out_val = 0;
uint_type significand = getNormalizedSignificand();
if (num_throwaway_bits <= 0) {
out_val = static_cast<other_uint_type>(significand);
uint_type shift_amount = static_cast<uint_type>(-num_throwaway_bits);
out_val = static_cast<other_uint_type>(out_val << shift_amount);
return out_val;
}
if ((significand & throwaway_mask) == 0) {
return static_cast<other_uint_type>(
negatable_right_shift(num_throwaway_bits, significand));
}
bool round_away_from_zero = false;
switch (dir) {
case kRoundToZero:
break;
case kRoundToPositiveInfinity:
round_away_from_zero = !isNegative();
break;
case kRoundToNegativeInfinity:
round_away_from_zero = isNegative();
break;
case kRoundToNearestEven:
if ((first_rounded_bit & significand) == 0) {
break;
}
if (((significand & throwaway_mask) & ~first_rounded_bit) != 0) {
round_away_from_zero = true;
break;
}
if ((significand & last_significant_bit) != 0) {
round_away_from_zero = true;
break;
}
break;
}
if (round_away_from_zero) {
return static_cast<other_uint_type>(
negatable_right_shift(num_throwaway_bits, incrementSignificand(
significand, last_significant_bit, carry_bit)));
} else {
return static_cast<other_uint_type>(
negatable_right_shift(num_throwaway_bits, significand));
}
}
template <typename other_T>
void castTo(other_T& other, round_direction round_dir) {
other = other_T(static_cast<typename other_T::native_type>(0));
bool negate = isNegative();
if (getUnsignedBits() == 0) {
if (negate) {
other.set_value(-other.value());
}
return;
}
uint_type significand = getSignificandBits();
bool carried = false;
typename other_T::uint_type rounded_significand =
getRoundedNormalizedSignificand<other_T>(round_dir, &carried);
int_type exponent = getUnbiasedExponent();
if (exponent == min_exponent) {
exponent = static_cast<int_type>(exponent + 1);
for (uint_type check_bit = first_exponent_bit >> 1; check_bit != 0;
check_bit = static_cast<uint_type>(check_bit >> 1)) {
exponent = static_cast<int_type>(exponent - 1);
if (check_bit & significand) break;
}
}
bool is_nan =
(getBits() & exponent_mask) == exponent_mask && significand != 0;
bool is_inf =
!is_nan &&
((exponent + carried) > static_cast<int_type>(other_T::exponent_bias) ||
(significand == 0 && (getBits() & exponent_mask) == exponent_mask));
if (is_inf) {
other.set_value(BitwiseCast<typename other_T::underlying_type>(
static_cast<typename other_T::uint_type>(
(negate ? other_T::sign_mask : 0) | other_T::exponent_mask)));
return;
}
if (is_nan) {
typename other_T::uint_type shifted_significand;
shifted_significand = static_cast<typename other_T::uint_type>(
negatable_left_shift(
static_cast<int_type>(other_T::num_fraction_bits) -
static_cast<int_type>(num_fraction_bits), significand));
other.set_value(BitwiseCast<typename other_T::underlying_type>(
static_cast<typename other_T::uint_type>(
(negate ? other_T::sign_mask : 0) | other_T::exponent_mask |
(shifted_significand == 0 ? 0x1 : shifted_significand))));
return;
}
bool round_underflow_up =
isNegative() ? round_dir == kRoundToNegativeInfinity
: round_dir == kRoundToPositiveInfinity;
typedef typename other_T::int_type other_int_type;
other.setFromSignUnbiasedExponentAndNormalizedSignificand(
negate, static_cast<other_int_type>(exponent), rounded_significand,
round_underflow_up);
return;
}
private:
T value_;
static_assert(num_used_bits ==
Traits::num_exponent_bits + Traits::num_fraction_bits + 1,
"The number of bits do not fit");
static_assert(sizeof(T) == sizeof(uint_type), "The type sizes do not match");
};
inline uint8_t get_nibble_from_character(int character) {
const char* dec = "0123456789";
const char* lower = "abcdef";
const char* upper = "ABCDEF";
const char* p = nullptr;
if ((p = strchr(dec, character))) {
return static_cast<uint8_t>(p - dec);
} else if ((p = strchr(lower, character))) {
return static_cast<uint8_t>(p - lower + 0xa);
} else if ((p = strchr(upper, character))) {
return static_cast<uint8_t>(p - upper + 0xa);
}
assert(false && "This was called with a non-hex character");
return 0;
}
template <typename T, typename Traits>
std::ostream& operator<<(std::ostream& os, const HexFloat<T, Traits>& value) {
typedef HexFloat<T, Traits> HF;
typedef typename HF::uint_type uint_type;
typedef typename HF::int_type int_type;
static_assert(HF::num_used_bits != 0,
"num_used_bits must be non-zero for a valid float");
static_assert(HF::num_exponent_bits != 0,
"num_exponent_bits must be non-zero for a valid float");
static_assert(HF::num_fraction_bits != 0,
"num_fractin_bits must be non-zero for a valid float");
const uint_type bits = spvutils::BitwiseCast<uint_type>(value.value());
const char* const sign = (bits & HF::sign_mask) ? "-" : "";
const uint_type exponent = static_cast<uint_type>(
(bits & HF::exponent_mask) >> HF::num_fraction_bits);
uint_type fraction = static_cast<uint_type>((bits & HF::fraction_encode_mask)
<< HF::num_overflow_bits);
const bool is_zero = exponent == 0 && fraction == 0;
const bool is_denorm = exponent == 0 && !is_zero;
int_type int_exponent = static_cast<int_type>(exponent - HF::exponent_bias);
int_exponent = is_zero ? 0 : int_exponent;
if (is_denorm) {
while ((fraction & HF::fraction_top_bit) == 0) {
fraction = static_cast<uint_type>(fraction << 1);
int_exponent = static_cast<int_type>(int_exponent - 1);
}
fraction = static_cast<uint_type>(fraction << 1);
fraction &= HF::fraction_represent_mask;
}
uint_type fraction_nibbles = HF::fraction_nibbles;
while (fraction_nibbles > 0 && (fraction & 0xF) == 0) {
fraction = static_cast<uint_type>(fraction >> 4);
--fraction_nibbles;
}
const auto saved_flags = os.flags();
const auto saved_fill = os.fill();
os << sign << "0x" << (is_zero ? '0' : '1');
if (fraction_nibbles) {
os << "." << std::setw(static_cast<int>(fraction_nibbles))
<< std::setfill('0') << std::hex << fraction;
}
os << "p" << std::dec << (int_exponent >= 0 ? "+" : "") << int_exponent;
os.flags(saved_flags);
os.fill(saved_fill);
return os;
}
template <typename T, typename Traits>
inline bool RejectParseDueToLeadingSign(std::istream& is, bool negate_value,
HexFloat<T, Traits>& value) {
if (negate_value) {
auto next_char = is.peek();
if (next_char == '-' || next_char == '+') {
value = HexFloat<T, Traits>(typename HexFloat<T, Traits>::uint_type(0));
is.setstate(std::ios_base::failbit);
return true;
}
}
return false;
}
template <typename T, typename Traits>
inline std::istream& ParseNormalFloat(std::istream& is, bool negate_value,
HexFloat<T, Traits>& value) {
if (RejectParseDueToLeadingSign(is, negate_value, value)) {
return is;
}
T val;
is >> val;
if (negate_value) {
val = -val;
}
value.set_value(val);
if (is.fail() && value.getUnsignedBits() == 0u) {
value = HexFloat<T, Traits>(typename HexFloat<T, Traits>::uint_type(0));
}
if (val.isInfinity()) {
value.set_value((value.isNegative() || negate_value) ? T::lowest()
: T::max());
is.setstate(std::ios_base::failbit);
}
return is;
}
template <>
inline std::istream&
ParseNormalFloat<FloatProxy<Float16>, HexFloatTraits<FloatProxy<Float16>>>(
std::istream& is, bool negate_value,
HexFloat<FloatProxy<Float16>, HexFloatTraits<FloatProxy<Float16>>>& value) {
HexFloat<FloatProxy<float>> float_val(0.0f);
ParseNormalFloat(is, negate_value, float_val);
float_val.castTo(value, kRoundToZero);
if (Float16::isInfinity(value.value().getAsFloat())) {
value.set_value(value.isNegative() ? Float16::lowest() : Float16::max());
is.setstate(std::ios_base::failbit);
}
return is;
}
template <typename T, typename Traits>
std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
using HF = HexFloat<T, Traits>;
using uint_type = typename HF::uint_type;
using int_type = typename HF::int_type;
value.set_value(static_cast<typename HF::native_type>(0.f));
if (is.flags() & std::ios::skipws) {
while (std::isspace(is.peek())) {
is.get();
}
}
auto next_char = is.peek();
bool negate_value = false;
if (next_char != '-' && next_char != '0') {
return ParseNormalFloat(is, negate_value, value);
}
if (next_char == '-') {
negate_value = true;
is.get();
next_char = is.peek();
}
if (next_char == '0') {
is.get();
auto maybe_hex_start = is.peek();
if (maybe_hex_start != 'x' && maybe_hex_start != 'X') {
is.unget();
return ParseNormalFloat(is, negate_value, value);
} else {
is.get();
}
} else {
return ParseNormalFloat(is, negate_value, value);
}
bool seen_p = false;
bool seen_dot = false;
uint_type fraction_index = 0;
uint_type fraction = 0;
int_type exponent = HF::exponent_bias;
while ((next_char = is.peek()) == '0') {
is.get();
}
bool is_denorm =
true;
bool bits_written = false;
while (!seen_p && !seen_dot) {
if (next_char == '.') {
seen_dot = true;
} else if (next_char == 'p') {
seen_p = true;
} else if (::isxdigit(next_char)) {
is_denorm = false;
int number = get_nibble_from_character(next_char);
for (int i = 0; i < 4; ++i, number <<= 1) {
uint_type write_bit = (number & 0x8) ? 0x1 : 0x0;
if (bits_written) {
fraction = static_cast<uint_type>(
fraction |
static_cast<uint_type>(
write_bit << (HF::top_bit_left_shift - fraction_index++)));
exponent = static_cast<int_type>(exponent + 1);
}
bits_written |= write_bit != 0;
}
} else {
is.setstate(std::ios::failbit);
return is;
}
is.get();
next_char = is.peek();
}
bits_written = false;
while (seen_dot && !seen_p) {
if (next_char == 'p') {
seen_p = true;
} else if (::isxdigit(next_char)) {
int number = get_nibble_from_character(next_char);
for (int i = 0; i < 4; ++i, number <<= 1) {
uint_type write_bit = (number & 0x8) ? 0x01 : 0x00;
bits_written |= write_bit != 0;
if (is_denorm && !bits_written) {
exponent = static_cast<int_type>(exponent - 1);
} else {
fraction = static_cast<uint_type>(
fraction |
static_cast<uint_type>(
write_bit << (HF::top_bit_left_shift - fraction_index++)));
}
}
} else {
is.setstate(std::ios::failbit);
return is;
}
is.get();
next_char = is.peek();
}
bool seen_sign = false;
int8_t exponent_sign = 1;
int_type written_exponent = 0;
while (true) {
if ((next_char == '-' || next_char == '+')) {
if (seen_sign) {
is.setstate(std::ios::failbit);
return is;
}
seen_sign = true;
exponent_sign = (next_char == '-') ? -1 : 1;
} else if (::isdigit(next_char)) {
written_exponent = static_cast<int_type>(written_exponent * 10);
written_exponent =
static_cast<int_type>(written_exponent + (next_char - '0'));
} else {
break;
}
is.get();
next_char = is.peek();
}
written_exponent = static_cast<int_type>(written_exponent * exponent_sign);
exponent = static_cast<int_type>(exponent + written_exponent);
bool is_zero = is_denorm && (fraction == 0);
if (is_denorm && !is_zero) {
fraction = static_cast<uint_type>(fraction << 1);
exponent = static_cast<int_type>(exponent - 1);
} else if (is_zero) {
exponent = 0;
}
if (exponent <= 0 && !is_zero) {
fraction = static_cast<uint_type>(fraction >> 1);
fraction |= static_cast<uint_type>(1) << HF::top_bit_left_shift;
}
fraction = (fraction >> HF::fraction_right_shift) & HF::fraction_encode_mask;
const int_type max_exponent =
SetBits<uint_type, 0, HF::num_exponent_bits>::get;
while (exponent < 0 && !is_zero) {
fraction = static_cast<uint_type>(fraction >> 1);
exponent = static_cast<int_type>(exponent + 1);
fraction &= HF::fraction_encode_mask;
if (fraction == 0) {
is_zero = true;
exponent = 0;
}
}
if (exponent > max_exponent) {
exponent = max_exponent;
fraction = 0;
}
uint_type output_bits = static_cast<uint_type>(
static_cast<uint_type>(negate_value ? 1 : 0) << HF::top_bit_left_shift);
output_bits |= fraction;
uint_type shifted_exponent = static_cast<uint_type>(
static_cast<uint_type>(exponent << HF::exponent_left_shift) &
HF::exponent_mask);
output_bits |= shifted_exponent;
T output_float = spvutils::BitwiseCast<T>(output_bits);
value.set_value(output_float);
return is;
}
template <typename T>
std::ostream& operator<<(std::ostream& os, const FloatProxy<T>& value) {
auto float_val = value.getAsFloat();
switch (std::fpclassify(float_val)) {
case FP_ZERO:
case FP_NORMAL: {
auto saved_precision = os.precision();
os.precision(std::numeric_limits<T>::digits10);
os << float_val;
os.precision(saved_precision);
} break;
default:
os << HexFloat<FloatProxy<T>>(value);
break;
}
return os;
}
template <>
inline std::ostream& operator<<<Float16>(std::ostream& os,
const FloatProxy<Float16>& value) {
os << HexFloat<FloatProxy<Float16>>(value);
return os;
}
}
#endif