Skip to content

Instantly share code, notes, and snippets.

@Randl
Last active July 28, 2017 06:04
Show Gist options
  • Save Randl/169004c2377fef7db28a418cc72e330c to your computer and use it in GitHub Desktop.
Save Randl/169004c2377fef7db28a418cc72e330c to your computer and use it in GitHub Desktop.
Half-precious class based on SSE F16C instructions
#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)));
}
}
@Maratyszcza
Copy link

The following changes would make it more efficient and portable:

half::half(float f) :
  _h(_mm_cvtsi128_si32(_mm_cvtps_ph(_mm_set_ss(f))))
{
}

half::operator float() const {
  return _mm_cvtss_f32(_mm_cvtph_ps(_mm_cvtsi32_si128(_h)));
}

@Randl
Copy link
Author

Randl commented Oct 21, 2016

@Maratyszcza thanks!

@edgarriba
Copy link

@Randl good job! Add proper documentation and go ahead with a PR 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment