// -*-C++-*- #ifndef FLOATPROPS_H #define FLOATPROPS_H #include "floattypes.h" #include #include #include #include namespace vecmathlib { // A structure describing various properties of a floating point // type. Most properties are already described in numeric_limits, so // we inherit it. template struct floatprops { // Some interesting properties are: // min // max // digits // epsilon // min_exponent // max_exponent // infinity // quiet_NaN }; // Properties of fp8 template <> struct floatprops { typedef fp8 real_t; typedef vml_std::int8_t int_t; typedef vml_std::uint8_t uint_t; static char const *name() { return "fp8"; } // Definitions that might come from numeric_limits<> instead: static real_t min() { __builtin_unreachable(); } static real_t max() { __builtin_unreachable(); } static int const digits = 4; static real_t epsilon() { __builtin_unreachable(); } static int const min_exponent = -6; static int const max_exponent = 7; static real_t infinity() { __builtin_unreachable(); } static real_t quiet_NaN() { __builtin_unreachable(); } // Ensure the sizes match static_assert(sizeof(real_t) == sizeof(int_t), "int_t has wrong size"); static_assert(sizeof(real_t) == sizeof(uint_t), "uint_t has wrong size"); // Number of bits in internal representation static int const bits = 8 * sizeof(real_t); static int const mantissa_bits = digits - 1; static int const signbit_bits = 1; static int const exponent_bits = bits - mantissa_bits - signbit_bits; static int const exponent_offset = 2 - min_exponent; static_assert(mantissa_bits + exponent_bits + signbit_bits == bits, "error in bit counts"); static uint_t const mantissa_mask = (uint_t(1) << mantissa_bits) - 1; static uint_t const exponent_mask = ((uint_t(1) << exponent_bits) - 1) << mantissa_bits; static uint_t const signbit_mask = uint_t(1) << (bits - 1); static_assert((mantissa_mask & exponent_mask & signbit_mask) == uint_t(0), "error in masks"); static_assert((mantissa_mask | exponent_mask | signbit_mask) == uint_t(~uint_t(0)), "error in masks"); // Re-interpret bit patterns static real_t as_float(int_t x) { real_t res; std::memcpy(&res, &x, sizeof res); return res; } static int_t as_int(real_t x) { int_t res; std::memcpy(&res, &x, sizeof res); return res; } static int_t replicate_byte(unsigned char byte) { int_t res; std::memset(&res, byte, sizeof res); return res; } // Convert values (truncate) static real_t convert_float(int_t x) { __builtin_unreachable(); } static int_t convert_int(real_t x) { __builtin_unreachable(); } }; // Properties of fp16 template <> struct floatprops { typedef fp16 real_t; typedef vml_std::int16_t int_t; typedef vml_std::uint16_t uint_t; static char const *name() { return "fp16"; } // Definitions that might come from numeric_limits<> instead: static real_t min() { __builtin_unreachable(); } static real_t max() { __builtin_unreachable(); } static int const digits = 11; static real_t epsilon() { __builtin_unreachable(); } static int const min_exponent = -14; static int const max_exponent = 15; static real_t infinity() { __builtin_unreachable(); } static real_t quiet_NaN() { __builtin_unreachable(); } // Ensure the sizes match static_assert(sizeof(real_t) == sizeof(int_t), "int_t has wrong size"); static_assert(sizeof(real_t) == sizeof(uint_t), "uint_t has wrong size"); // Number of bits in internal representation static int const bits = 8 * sizeof(real_t); static int const mantissa_bits = digits - 1; static int const signbit_bits = 1; static int const exponent_bits = bits - mantissa_bits - signbit_bits; static int const exponent_offset = 2 - min_exponent; static_assert(mantissa_bits + exponent_bits + signbit_bits == bits, "error in bit counts"); static uint_t const mantissa_mask = (uint_t(1) << mantissa_bits) - 1; static uint_t const exponent_mask = ((uint_t(1) << exponent_bits) - 1) << mantissa_bits; static uint_t const signbit_mask = uint_t(1) << (bits - 1); static_assert((mantissa_mask & exponent_mask & signbit_mask) == uint_t(0), "error in masks"); static_assert((mantissa_mask | exponent_mask | signbit_mask) == uint_t(~uint_t(0)), "error in masks"); // Re-interpret bit patterns static real_t as_float(int_t x) { real_t res; std::memcpy(&res, &x, sizeof res); return res; } static int_t as_int(real_t x) { int_t res; std::memcpy(&res, &x, sizeof res); return res; } static int_t replicate_byte(unsigned char byte) { int_t res; std::memset(&res, byte, sizeof res); return res; } // Convert values (truncate) static real_t convert_float(int_t x) { __builtin_unreachable(); } static int_t convert_int(real_t x) { __builtin_unreachable(); } }; // Properties of float template <> struct floatprops : std::numeric_limits { typedef float real_t; typedef vml_std::int32_t int_t; typedef vml_std::uint32_t uint_t; static char const *name() { return "float"; } // Ensure the internal representation is what we expect static_assert(is_signed, "real_t is not signed"); static_assert(radix == 2, "real_t is not binary"); // Ensure the sizes match static_assert(sizeof(real_t) == sizeof(int_t), "int_t has wrong size"); static_assert(sizeof(real_t) == sizeof(uint_t), "uint_t has wrong size"); // Number of bits in internal representation static int const bits = 8 * sizeof(real_t); static int const mantissa_bits = digits - 1; static int const signbit_bits = 1; static int const exponent_bits = bits - mantissa_bits - signbit_bits; static int const exponent_offset = 2 - min_exponent; static_assert(mantissa_bits + exponent_bits + signbit_bits == bits, "error in bit counts"); static uint_t const mantissa_mask = (uint_t(1) << mantissa_bits) - 1; static uint_t const exponent_mask = ((uint_t(1) << exponent_bits) - 1) << mantissa_bits; static uint_t const signbit_mask = uint_t(1) << (bits - 1); static_assert((mantissa_mask & exponent_mask & signbit_mask) == uint_t(0), "error in masks"); static_assert((mantissa_mask | exponent_mask | signbit_mask) == uint_t(~uint_t(0)), "error in masks"); // Re-interpret bit patterns static real_t as_float(int_t x) { real_t res; std::memcpy(&res, &x, sizeof res); return res; } static int_t as_int(real_t x) { int_t res; std::memcpy(&res, &x, sizeof res); return res; } static int_t replicate_byte(unsigned char byte) { int_t res; std::memset(&res, byte, sizeof res); return res; } // Convert values (truncate) static real_t convert_float(int_t x) { return real_t(x); } static int_t convert_int(real_t x) { return int_t(x); } }; // Properties of double template <> struct floatprops : std::numeric_limits { typedef double real_t; typedef vml_std::int64_t int_t; typedef vml_std::uint64_t uint_t; static char const *name() { return "double"; } // Ensure the internal representation is what we expect static_assert(is_signed, "real_t is not signed"); static_assert(radix == 2, "real_t is not binary"); // Ensure the sizes match static_assert(sizeof(real_t) == sizeof(int_t), "int_t has wrong size"); static_assert(sizeof(real_t) == sizeof(uint_t), "uint_t has wrong size"); // Number of bits in internal representation static int const bits = 8 * sizeof(real_t); static int const mantissa_bits = digits - 1; static int const signbit_bits = 1; static int const exponent_bits = bits - mantissa_bits - signbit_bits; static int const exponent_offset = 2 - min_exponent; static_assert(mantissa_bits + exponent_bits + signbit_bits == bits, "error in bit counts"); static uint_t const mantissa_mask = (uint_t(1) << mantissa_bits) - 1; static uint_t const exponent_mask = ((uint_t(1) << exponent_bits) - 1) << mantissa_bits; static uint_t const signbit_mask = uint_t(1) << (bits - 1); static_assert((mantissa_mask & exponent_mask & signbit_mask) == uint_t(0), "error in masks"); static_assert((mantissa_mask | exponent_mask | signbit_mask) == uint_t(~uint_t(0)), "error in masks"); // Re-interpret bit patterns static real_t as_float(int_t x) { real_t res; std::memcpy(&res, &x, sizeof res); return res; } static int_t as_int(real_t x) { int_t res; std::memcpy(&res, &x, sizeof res); return res; } static int_t replicate_byte(unsigned char byte) { int_t res; std::memset(&res, byte, sizeof res); return res; } // Convert values (truncate) static real_t convert_float(int_t x) { return real_t(x); } static int_t convert_int(real_t x) { return int_t(x); } }; // We are adding the (unused) type RV here to avoid name mangling // problems. On some systems, the vector size does not enter into // the mangled name (!), leading to duplicate function definitions. template E get_elt(const V &v, const int n) { const size_t s = sizeof(E); E e; // assert(n>=0 and s*n V &set_elt(V &v, const int n, const E e) { const size_t s = sizeof(E); // assert(n>=0 and s*n real_t barrier(real_t x) { #if defined __GNUC__ && !defined __clang__ && !defined __ICC // GCC crashes when +X is used as constraint #if defined __SSE2__ __asm__("" : "+x"(x)); #elif defined __PPC64__ // maybe also __PPC__ __asm__("" : "+f"(x)); #elif defined __arm__ __asm__("" : "+w"(x)); #else #error "Floating point barrier undefined on this architecture" #endif #elif defined __clang__ __asm__("" : "+x"(x)); #elif defined __ICC __asm__("" : "+x"(x)); #elif defined __IBMCPP__ __asm__("" : "+f"(x)); #else #error "Floating point barrier undefined on this architecture" #endif return x; } } // namespace vecmathlib #endif // #ifndef FLOATPROPS_H