Last active
July 28, 2017 06:04
-
-
Save Randl/169004c2377fef7db28a418cc72e330c to your computer and use it in GitHub Desktop.
Half-precious class based on SSE F16C instructions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdint> | |
#include <immintrin.h> | |
constexpr uint_least16_t CNN_HALF_SIGN = 1 << 15; | |
constexpr uint_least16_t CNN_HALF_EXPONENT = 0x1F << 10; | |
constexpr uint_least16_t CNN_HALF_MANTISSA = 0x3FF; | |
struct half; | |
namespace std { | |
const half abs(const half &h); | |
} | |
struct half { | |
// Constructors | |
constexpr half() : _h() {}; // no initialization | |
half(float f) : _h(_mm_cvtsi128_si32(_mm_cvtps_ph(_mm_set_ss(f), 0))) {}; | |
half(double d) : _h(_mm_cvtsi128_si32(_mm_cvtps_ph(_mm_set_ss(float(d)), | |
0))) {}; | |
half(uint_least16_t u) : _h(u) {}; | |
// checks | |
bool isFinite() const; | |
bool isNormalized() const; | |
bool isDenormalized() const; | |
bool isZero() const; | |
bool isNan() const; | |
bool isInfinity() const; | |
bool isNegative() const; | |
//operators | |
half operator+() const; // unary + | |
half operator-() const; // unary - | |
bool operator!() const; // logical not | |
void operator+=(const half &i); | |
void operator-=(const half &i); | |
void operator/=(const half &i); | |
void operator*=(const half &i); | |
friend const half operator+(const half &left, const half &right); | |
friend const half operator-(const half &left, const half &right); | |
friend const half operator/(const half &left, const half &right); | |
friend const half operator*(const half &left, const half &right); | |
friend const bool operator==(const half &left, const half &right); | |
friend const bool operator!=(const half &left, const half &right); | |
friend const bool operator>(const half &left, const half &right); | |
friend const bool operator<(const half &left, const half &right); | |
friend const bool operator>=(const half &left, const half &right); | |
friend const bool operator<=(const half &left, const half &right); | |
friend const bool operator&&(const half &left, const half &right); | |
friend const bool operator||(const half &left, const half &right); | |
friend std::ostream &operator<<(std::ostream &os, const half &h); | |
friend std::istream &operator>>(std::istream &is, half &h); | |
operator double() const; | |
operator float() const; | |
operator int() const; | |
operator bool() const; | |
//operations | |
friend const half std::abs(const half &h); | |
private: | |
uint_least16_t _h; | |
}; | |
half half::operator+() const { | |
return *this; | |
} | |
half half::operator-() const { | |
return half(uint_least16_t(_h ^ CNN_HALF_SIGN)); | |
} | |
bool half::operator!() const { | |
return (_h & (~CNN_HALF_SIGN)) == 0; | |
} | |
void half::operator+=(const half &i) { | |
*this = *this + i; | |
return; | |
} | |
void half::operator-=(const half &i) { | |
*this = *this - i; | |
return; | |
} | |
void half::operator/=(const half &i) { | |
*this = *this / i; | |
return; | |
} | |
void half::operator*=(const half &i) { | |
*this = *this * i; | |
return; | |
} | |
const half operator+(const half &left, const half &right) { | |
auto z = _mm_cvtph_ps(_mm_set_epi16(0, 0, 0, 0, 0, 0, right._h, left._h)); | |
return half(z[0] + z[1]); | |
} | |
const half operator-(const half &left, const half &right) { | |
auto z = _mm_cvtph_ps(_mm_set_epi16(0, 0, 0, 0, 0, 0, right._h, left._h)); | |
return half(z[0] - z[1]); | |
} | |
const half operator/(const half &left, const half &right) { | |
auto z = _mm_cvtph_ps(_mm_set_epi16(0, 0, 0, 0, 0, 0, right._h, left._h)); | |
return half(z[0] / z[1]); | |
} | |
const half operator*(const half &left, const half &right) { | |
auto z = _mm_cvtph_ps(_mm_set_epi16(0, 0, 0, 0, 0, 0, right._h, left._h)); | |
return half(z[0] * z[1]); | |
} | |
const bool operator==(const half &left, const half &right) { | |
return left._h == right._h; | |
} | |
const bool operator!=(const half &left, const half &right) { | |
return left._h != right._h; | |
} | |
const bool operator>(const half &left, const half &right) { | |
auto z = _mm_cvtph_ps(_mm_set_epi16(0, 0, 0, 0, 0, 0, right._h, left._h)); | |
return z[0] > z[1]; | |
} | |
const bool operator<(const half &left, const half &right) { | |
auto z = _mm_cvtph_ps(_mm_set_epi16(0, 0, 0, 0, 0, 0, right._h, left._h)); | |
return z[0] < z[1]; | |
} | |
const bool operator>=(const half &left, const half &right) { | |
auto z = _mm_cvtph_ps(_mm_set_epi16(0, 0, 0, 0, 0, 0, right._h, left._h)); | |
return z[0] >= z[1]; | |
} | |
const bool operator<=(const half &left, const half &right) { | |
auto z = _mm_cvtph_ps(_mm_set_epi16(0, 0, 0, 0, 0, 0, right._h, left._h)); | |
return z[0] <= z[1]; | |
} | |
const bool operator&&(const half &left, const half &right) { | |
return bool(left) && bool(right); | |
} | |
const bool operator||(const half &left, const half &right) { | |
return bool(left) || bool(right); | |
} | |
std::ostream &operator<<(std::ostream &os, const half &h) { | |
os << float(h); | |
return os; | |
} | |
std::istream &operator>>(std::istream &is, half &h) { | |
float x; | |
is >> x; | |
h = half(x); | |
return is; | |
} | |
half::operator double() const { | |
return _mm_cvtss_f32(_mm_cvtph_ps(_mm_cvtsi32_si128(_h))); | |
} | |
half::operator float() const { | |
return _mm_cvtss_f32(_mm_cvtph_ps(_mm_cvtsi32_si128(_h))); | |
} | |
half::operator int() const { | |
return int(float(*this)); | |
} | |
half::operator bool() const { | |
return (_h & (~CNN_HALF_SIGN)) != 0; | |
} | |
bool half::isFinite() const { | |
return ((_h & CNN_HALF_EXPONENT) ^ CNN_HALF_EXPONENT) != 0; | |
} | |
bool half::isNormalized() const { | |
return this->isFinite() && !this->isDenormalized(); | |
} | |
bool half::isDenormalized() const { | |
return this->isFinite() && (_h & CNN_HALF_EXPONENT) == 0; | |
} | |
bool half::isZero() const { | |
return !bool(*this); | |
} | |
bool half::isNan() const { | |
return !this->isFinite() && (_h & CNN_HALF_MANTISSA) != 0; | |
} | |
bool half::isInfinity() const { | |
return !this->isFinite() && (_h & CNN_HALF_MANTISSA) == 0; | |
} | |
bool half::isNegative() const { | |
return (_h & CNN_HALF_SIGN) != 0; | |
} | |
//literal | |
half operator""_h(long double d) { | |
return half(double(d)); | |
} | |
namespace std { | |
const half abs(const half &h) { | |
return half(uint_least16_t(h._h & (~CNN_HALF_SIGN))); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The following changes would make it more efficient and portable: