Skip to content

Instantly share code, notes, and snippets.

@AltimorTASDK
Created February 3, 2025 02:13
Show Gist options
  • Save AltimorTASDK/2cdb847904db84fd73f4638e13d933aa to your computer and use it in GitHub Desktop.
Save AltimorTASDK/2cdb847904db84fd73f4638e13d933aa to your computer and use it in GitHub Desktop.
#include <bit>
#include <climits>
#include <concepts>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <limits>
#include <type_traits>
#include <utility>
constexpr auto to_unsigned(std::integral auto value)
{
return static_cast<std::make_unsigned_t<decltype(value)>>(value);
}
template<typename To, typename From> requires(sizeof(From) == sizeof(To))
constexpr auto bit_cast_any(const From &src)
{
return std::bit_cast<To>(src);
}
template<typename To, typename From> requires(sizeof(From) > sizeof(To))
constexpr auto bit_cast_any(const From &src)
{
struct holder {
To value;
char pad[sizeof(From) - sizeof(To)];
};
return std::bit_cast<holder>(src).value;
}
template<typename To, typename From> requires(sizeof(From) < sizeof(To))
constexpr auto bit_cast_any(const From &src)
{
const struct {
From value;
char pad[sizeof(To) - sizeof(From)];
} copy = { src };
return std::bit_cast<To>(copy);
}
template<size_t BitSize>
class bitmask {
template<size_t OtherBitSize>
friend class bitmask;
public:
static constexpr auto bit_size = BitSize;
static constexpr auto byte_size = (BitSize + CHAR_BIT - 1) / CHAR_BIT;
private:
using word_type = uintptr_t;
static constexpr auto word_bits = sizeof(word_type) * CHAR_BIT;
static constexpr auto word_count = (byte_size + sizeof(word_type) - 1) / sizeof(word_type);
static constexpr auto word_indices = std::make_index_sequence<word_count>{};
word_type words[word_count] = { 0 };
public:
constexpr bitmask() = default;
template<typename T>
explicit(!std::integral<T> || sizeof(T) * CHAR_BIT > BitSize)
constexpr bitmask(T value)
{
*this = bit_cast_any<bitmask>(value);
if constexpr (sizeof(T) * CHAR_BIT > BitSize)
constrain_bit_size();
}
template<std::same_as<word_type> ...T> requires (sizeof...(T) == word_count)
constexpr bitmask(T ...values) : words { values... }
{
constrain_bit_size();
}
template<size_t NewBitSize>
explicit(BitSize > NewBitSize)
constexpr operator bitmask<NewBitSize>() const
{
return [&]<auto ...I>(std::index_sequence<I...>) {
return bitmask { get_word(I)... };
}(bitmask<NewBitSize>::word_indices);
}
template<std::integral T>
explicit(byte_size > sizeof(T))
constexpr operator T() const
{
return bit_cast_any<T>(*this);
}
private:
constexpr void constrain_bit_size()
{
words[word_count-1] &= ~word_type{0} >> (word_bits * word_count - BitSize);
}
constexpr auto get_word(size_t index) const
{
return index >= 0 && index < word_count ? words[index] : 0;
}
public:
constexpr bitmask operator<<(size_t shift) const
{
const auto wordshift = shift / word_bits;
const auto bitshift = shift % word_bits;
const auto invshift = word_bits - bitshift;
return [&]<auto ...I>(std::index_sequence<I...>) {
if (bitshift == 0) {
return bitmask { get_word(I - wordshift)... };
} else {
return bitmask { (get_word(I - wordshift) << bitshift) |
(get_word(I - wordshift - 1) >> invshift)... };
}
}(word_indices);
}
constexpr bitmask operator>>(size_t shift) const
{
const auto wordshift = shift / word_bits;
const auto bitshift = shift % word_bits;
const auto invshift = word_bits - bitshift;
return [&]<auto ...I>(std::index_sequence<I...>) {
if (bitshift == 0) {
return bitmask { get_word(I + wordshift)... };
} else {
return bitmask { (get_word(I + wordshift) >> bitshift) |
(get_word(I + wordshift + 1) << invshift)... };
}
}(word_indices);
}
constexpr bitmask operator&(const bitmask &b) const
{
return [&]<auto ...I>(std::index_sequence<I...>) {
return bitmask { words[I] & b.words[I]... };
}(word_indices);
}
constexpr bitmask operator|(const bitmask &b) const
{
return [&]<auto ...I>(std::index_sequence<I...>) {
return bitmask { words[I] | b.words[I]... };
}(word_indices);
}
constexpr bitmask operator^(const bitmask &b) const
{
return [&]<auto ...I>(std::index_sequence<I...>) {
return bitmask { words[I] ^ b.words[I]... };
}(word_indices);
}
constexpr bitmask operator~() const
{
return [&]<auto ...I>(std::index_sequence<I...>) {
return bitmask { ~words[I]... };
}(word_indices);
}
constexpr bool operator==(const bitmask &b) const = default;
constexpr bool operator!=(const bitmask &b) const = default;
};
// CTAD
bitmask(auto value) -> bitmask<sizeof(value) * CHAR_BIT>;
template<typename T>
concept BitMask = requires(T t) { []<size_t N>(bitmask<N>){}(t); };
constexpr auto operator&(const auto &a, const BitMask auto &b)
requires std::convertible_to<decltype(a), decltype(b)>
{ return static_cast<decltype(b)>(a) & b; }
constexpr auto operator|(const auto &a, const BitMask auto &b)
requires std::convertible_to<decltype(a), decltype(b)>
{ return static_cast<decltype(b)>(a) | b; }
constexpr auto operator^(const auto &a, const BitMask auto &b)
requires std::convertible_to<decltype(a), decltype(b)>
{ return static_cast<decltype(b)>(a) ^ b; }
constexpr auto operator==(const auto &a, const BitMask auto &b)
requires std::convertible_to<decltype(a), decltype(b)>
{ return static_cast<decltype(b)>(a) == b; }
constexpr auto operator!=(const auto &a, const BitMask auto &b)
requires std::convertible_to<decltype(a), decltype(b)>
{ return static_cast<decltype(b)>(a) != b; }
template<std::floating_point Float, std::integral Integral, size_t Shift = 0>
requires (std::numeric_limits<Float>::is_iec559 &&
std::numeric_limits<Float>::radix == 2)
constexpr Float integral_to_float(Integral value)
{
using float_bitmask = bitmask<sizeof(Float) * CHAR_BIT>;
constexpr auto int_sign = std::numeric_limits<Integral>::min();
constexpr auto sig_bits = std::numeric_limits<Float>::digits - 1;
constexpr auto sig_mask = ~(~float_bitmask(0) << sig_bits);
constexpr auto exp_base = std::numeric_limits<Float>::max_exponent - 1;
// Check if type requires explicit leading one in significand
constexpr auto one_mask = bitmask(Float{1}) & bitmask(Float{2});
constexpr auto exp_shift = one_mask == 0 ? sig_bits : sig_bits + 1;
// Set exponent such that increasing the significand by 1 increases the value by 1 << Shift
constexpr auto exp = float_bitmask(exp_base + Shift + sig_bits) << exp_shift;
constexpr auto sign = float_bitmask(to_unsigned(int_sign) >> Shift) & sig_mask;
constexpr auto scale = exp | one_mask | sign;
const auto sig = float_bitmask(to_unsigned(value) >> Shift) & sig_mask;
const auto result = bit_cast_any<Float>(scale ^ sig) - bit_cast_any<Float>(scale);
if constexpr (Shift + sig_bits >= sizeof(Integral) * CHAR_BIT)
return result;
else
return result + integral_to_float<Float, Integral, Shift + sig_bits>(value);
}
template<typename Float>
[[gnu::noinline]] auto i2f_sw(auto value)
{
return integral_to_float<Float>(value);
}
template<typename Float>
[[gnu::noinline]] auto i2f_hw(auto value)
{
return static_cast<Float>(value);
}
template<typename Float>
void print_test(auto value)
{
std::cout << std::setw(20) << value;
std::cout << " -> software " << std::setw(22) << i2f_sw<Float>(value) << std::endl;
std::cout << std::setw(20) << "";
std::cout << " hardware " << std::setw(22) << i2f_hw<Float>(value) << std::endl;
}
int main()
{
std::cout << std::fixed << std::setprecision(1);
print_test<long double>(12345678901234567890u);
print_test<double>(12345678901234567890u);
print_test<double>(12345678901234567u);
print_test<double>(1234567890123456);
print_test<double>(12345678);
print_test<float>(12345678);
print_test<float>(-123);
print_test<float>(-1u);
print_test<float>(12345678901234567890u);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment