Skip to content

Commit

Permalink
enable quad precision floating point component extraction (#336)
Browse files Browse the repository at this point in the history
* enable quad precision floating point component extraction

* fix argument order for memcpy

Signed-off-by: yboettcher <39460066+yboettcher@users.noreply.github.com>

---------

Signed-off-by: yboettcher <39460066+yboettcher@users.noreply.github.com>
  • Loading branch information
yboettcher committed May 4, 2023
1 parent 40ef3ad commit 6833046
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 17 deletions.
28 changes: 23 additions & 5 deletions include/universal/internal/value/value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,22 +277,40 @@ class value {
case FP_NORMAL:
{
long double _fr{0};
unsigned long long _63b_fraction_without_hidden_bit{0};
int _exponent{0};
extract_fp_components(rhs, _sign, _exponent, _fr, _63b_fraction_without_hidden_bit);
_scale = _exponent - 1;
// fraction without hidden bit moved into the if

// how to interpret the fraction bits: TODO: this should be a static compile-time code block
if (sizeof(long double) == 8) {
if constexpr (sizeof(long double) == 8) {
// we are just a double and thus only have 52bits of fraction

std::uint64_t _63b_fraction_without_hidden_bit{0};
extract_fp_components(rhs, _sign, _exponent, _fr, _63b_fraction_without_hidden_bit);
_scale = _exponent - 1;

_fraction = extract_52b_fraction<fbits>(_63b_fraction_without_hidden_bit);
if (_trace_value_conversion) std::cout << "long double " << rhs << " sign " << _sign << " scale " << _scale << " 52b fraction 0x" << std::hex << _63b_fraction_without_hidden_bit << " _fraction b" << _fraction << std::dec << std::endl;

}
else if (sizeof(long double) == 16) {
else if constexpr (sizeof(long double) == 16 && std::numeric_limits<long double>::digits <= 64) {
// how to differentiate between 80bit and 128bit formats?

std::uint64_t _63b_fraction_without_hidden_bit{0};
extract_fp_components(rhs, _sign, _exponent, _fr, _63b_fraction_without_hidden_bit);
_scale = _exponent - 1;

_fraction = extract_63b_fraction<fbits>(_63b_fraction_without_hidden_bit);
if (_trace_value_conversion) std::cout << "long double " << rhs << " sign " << _sign << " scale " << _scale << " 63b fraction 0x" << std::hex << _63b_fraction_without_hidden_bit << " _fraction b" << _fraction << std::dec << std::endl;

} else if constexpr (sizeof(long double) == 16 && std::numeric_limits<long double>::digits <= 128) {
internal::uint128 _112b_fraction_without_hidden_bit{0};
extract_fp_components(rhs, _sign, _exponent, _fr, _112b_fraction_without_hidden_bit);
_scale = _exponent - 1;

_fraction = extract_long_double_fraction<fbits>(&_112b_fraction_without_hidden_bit);
if (_trace_value_conversion) std::cout << "long double " << rhs << " sign " << _sign << " scale " << _scale << " 112b fraction upper 0x" << std::hex << _112b_fraction_without_hidden_bit.upper << " lower 0x" << std::hex << _112b_fraction_without_hidden_bit.lower << " _fraction b" << _fraction << std::dec << std::endl;
} else {
assert(false);
}
_nrOfBits = fbits;
}
Expand Down
16 changes: 16 additions & 0 deletions include/universal/native/nonconstexpr/extract_fp_components.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
#include <cmath>
#include <universal/internal/bitblock/bitblock.hpp>

/*
* The frexpf/frexp/frexpl functions have become constexpr in C++23. Universal is using the <bit> library
Expand All @@ -30,6 +31,7 @@ inline void extract_fp_components(double fp, bool& _sign, int& _exponent, double
// this implementation assumes an 80bit extended precision representation
// TODO: support a full quad precision long double
inline void extract_fp_components(long double fp, bool& _sign, int& _exponent, long double& _fr, std::uint64_t& _fraction) {
assert(sizeof(long double) == 8 || sizeof(long double) == 16 && std::numeric_limits<long double>::digits <= 64);
if constexpr (sizeof(long double) == 8) { // check if (long double) is aliased to be just a double
_sign = fp < 0.0;
_fr = (long double)(::std::frexp(double(fp), &_exponent));
Expand All @@ -42,6 +44,20 @@ inline void extract_fp_components(long double fp, bool& _sign, int& _exponent, l
}
}

inline void extract_fp_components(long double fp, bool& _sign, int& _exponent, long double& _fr, internal::uint128& _fraction) {
assert(sizeof(long double) == 16 && std::numeric_limits<long double>::digits > 64);
_sign = fp < 0.0;
_fr = ::std::frexp(fp, &_exponent);

std::memcpy(&_fraction, &_fr, sizeof(internal::uint128)); // _fraction = reinterpret_cast<uint128>(_fr);

// we need to remove the upper bits that are not part of the mantissa. (all bits - mantissa bits - 1). -1 because the first bit is not stored
// we only need to do this on the upper part of the uint128, as we asserted that the mantissa has more than 64 bits.
constexpr int shift = 8 * sizeof(long double) - (std::numeric_limits<long double>::digits - 1);
_fraction.upper <<= shift;
_fraction.upper >>= shift;
}

}} // namespace sw::universal


41 changes: 29 additions & 12 deletions tools/cmd/propenv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,19 +146,36 @@ try {
bool sign = false;
int scale = 0;
long double fr = 0;
unsigned long long fraction = 0;
sw::universal::extract_fp_components(ld.da, sign, scale, fr, fraction);

std::cout << "value " << std::setprecision(q_prec) << ld.da << std::setprecision(f_prec) << '\n';
std::cout << "hex ";
std::cout << std::hex << std::setfill('0');
for (int i = 15; i >= 0; i--) {
std::cout << std::setw(2) << (int)(uint8_t)ld.bytes[i] << " ";
if constexpr (std::numeric_limits<long double>::digits <= 64) {
std::uint64_t fraction = 0;
sw::universal::extract_fp_components(ld.da, sign, scale, fr, fraction);

std::cout << "value " << std::setprecision(q_prec) << ld.da << std::setprecision(f_prec) << '\n';
std::cout << "hex ";
std::cout << std::hex << std::setfill('0');
for (int i = 15; i >= 0; i--) {
std::cout << std::setw(2) << (int)(uint8_t)ld.bytes[i] << " ";
}
std::cout << std::dec << std::setfill(' ') << '\n';
std::cout << "sign " << (sign ? "-" : "+") << '\n';
std::cout << "scale " << scale << '\n';
std::cout << "fraction " << fraction << '\n';
} else {
sw::universal::internal::uint128 fraction{0};
sw::universal::extract_fp_components(ld.da, sign, scale, fr, fraction);

std::cout << "value " << std::setprecision(q_prec) << ld.da << std::setprecision(f_prec) << '\n';
std::cout << "hex ";
std::cout << std::hex << std::setfill('0');
for (int i = 15; i >= 0; i--) {
std::cout << std::setw(2) << (int)(uint8_t)ld.bytes[i] << " ";
}
std::cout << std::dec << std::setfill(' ') << '\n';
std::cout << "sign " << (sign ? "-" : "+") << '\n';
std::cout << "scale " << scale << '\n';
std::cout << "fraction upper" << fraction.upper << '\n';
std::cout << "fraction lower" << fraction.lower << '\n';
}
std::cout << std::dec << std::setfill(' ') << '\n';
std::cout << "sign " << (sign ? "-" : "+") << '\n';
std::cout << "scale " << scale << '\n';
std::cout << "fraction " << fraction << '\n';

std::cout << std::endl;

Expand Down

0 comments on commit 6833046

Please sign in to comment.