//////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2017, MINRES Technologies GmbH // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // Contributors: // eyck@minres.com - initial API and implementation //////////////////////////////////////////////////////////////////////////////// #include "fp_functions.h" #include extern "C" { #include "internals.h" #include "specialize.h" #include } #include using this_t = uint8_t*; // this does not inlcude any reserved rm or the DYN rm, as DYN rm should be taken care of in the vm_impl const std::array rmm_map = {softfloat_round_near_even /*RNE*/, softfloat_round_minMag /*RTZ*/, softfloat_round_min /*RDN*/, softfloat_round_max /*RUP?*/, softfloat_round_near_maxMag /*RMM*/, 0 /*reserved*/, softfloat_round_odd /*ROD*/}; const uint32_t quiet_nan32 = 0x7fC00000; extern "C" { uint32_t fget_flags() { return softfloat_exceptionFlags & 0x1f; } uint16_t fsqrt_h(uint16_t v1, uint8_t mode) { float16_t v1f{v1}; softfloat_roundingMode = rmm_map.at(mode); softfloat_exceptionFlags = 0; float16_t r = f16_sqrt(v1f); return r.v; } uint16_t fclass_h(uint16_t v1) { float16_t a{v1}; union ui16_f16 uA; uint_fast16_t uiA; uA.f = a; uiA = uA.ui; bool infOrNaN = expF16UI(uiA) == 0xFF; bool subnormalOrZero = expF16UI(uiA) == 0; bool sign = signF16UI(uiA); bool fracZero = fracF16UI(uiA) == 0; bool isNaN = isNaNF16UI(uiA); bool isSNaN = softfloat_isSigNaNF16UI(uiA); return (sign && infOrNaN && fracZero) << 0 | (sign && !infOrNaN && !subnormalOrZero) << 1 | (sign && subnormalOrZero && !fracZero) << 2 | (sign && subnormalOrZero && fracZero) << 3 | (!sign && infOrNaN && fracZero) << 7 | (!sign && !infOrNaN && !subnormalOrZero) << 6 | (!sign && subnormalOrZero && !fracZero) << 5 | (!sign && subnormalOrZero && fracZero) << 4 | (isNaN && isSNaN) << 8 | (isNaN && !isSNaN) << 9; } uint32_t fadd_s(uint32_t v1, uint32_t v2, uint8_t mode) { float32_t v1f{v1}, v2f{v2}; softfloat_roundingMode = rmm_map.at(mode); softfloat_exceptionFlags = 0; float32_t r = f32_add(v1f, v2f); return r.v; } uint32_t fsub_s(uint32_t v1, uint32_t v2, uint8_t mode) { float32_t v1f{v1}, v2f{v2}; softfloat_roundingMode = rmm_map.at(mode); softfloat_exceptionFlags = 0; float32_t r = f32_sub(v1f, v2f); return r.v; } uint32_t fmul_s(uint32_t v1, uint32_t v2, uint8_t mode) { float32_t v1f{v1}, v2f{v2}; softfloat_roundingMode = rmm_map.at(mode); softfloat_exceptionFlags = 0; float32_t r = f32_mul(v1f, v2f); return r.v; } uint32_t fdiv_s(uint32_t v1, uint32_t v2, uint8_t mode) { float32_t v1f{v1}, v2f{v2}; softfloat_roundingMode = rmm_map.at(mode); softfloat_exceptionFlags = 0; float32_t r = f32_div(v1f, v2f); return r.v; } uint32_t fsqrt_s(uint32_t v1, uint8_t mode) { float32_t v1f{v1}; softfloat_roundingMode = rmm_map.at(mode); softfloat_exceptionFlags = 0; float32_t r = f32_sqrt(v1f); return r.v; } uint32_t fcmp_s(uint32_t v1, uint32_t v2, uint32_t op) { float32_t v1f{v1}, v2f{v2}; softfloat_exceptionFlags = 0; bool nan = (v1 & defaultNaNF32UI) == quiet_nan32 || (v2 & defaultNaNF32UI) == quiet_nan32; bool snan = softfloat_isSigNaNF32UI(v1) || softfloat_isSigNaNF32UI(v2); switch(op) { case 0: if(nan | snan) { if(snan) softfloat_raiseFlags(softfloat_flag_invalid); return 0; } else return f32_eq(v1f, v2f) ? 1 : 0; case 1: if(nan | snan) { softfloat_raiseFlags(softfloat_flag_invalid); return 0; } else return f32_le(v1f, v2f) ? 1 : 0; case 2: if(nan | snan) { softfloat_raiseFlags(softfloat_flag_invalid); return 0; } else return f32_lt(v1f, v2f) ? 1 : 0; default: break; } return -1; } uint32_t fcvt_s(uint32_t v1, uint32_t op, uint8_t mode) { float32_t v1f{v1}; softfloat_exceptionFlags = 0; float32_t r; switch(op) { case 0: { // FCVT__W__S uint_fast32_t res = f32_to_i32(v1f, rmm_map.at(mode), true); return (uint32_t)res; } case 1: { // FCVT__WU__S uint_fast32_t res = f32_to_ui32(v1f, rmm_map.at(mode), true); return (uint32_t)res; } case 2: // FCVT__S__W r = i32_to_f32((int32_t)v1); return r.v; case 3: // FCVT__S__WU r = ui32_to_f32(v1); return r.v; } return 0; } uint32_t fmadd_s(uint32_t v1, uint32_t v2, uint32_t v3, uint32_t op, uint8_t mode) { uint32_t F32_SIGN = 1UL << 31; switch(op) { case 0: // FMADD_S break; case 1: // FMSUB_S v3 ^= F32_SIGN; break; case 2: // FNMADD_S v1 ^= F32_SIGN; v3 ^= F32_SIGN; break; case 3: // FNMSUB_S v1 ^= F32_SIGN; break; } softfloat_roundingMode = rmm_map.at(mode); softfloat_exceptionFlags = 0; float32_t res = softfloat_mulAddF32(v1, v2, v3, 0); return res.v; } uint32_t fsel_s(uint32_t v1, uint32_t v2, uint32_t op) { softfloat_exceptionFlags = 0; bool v1_nan = (v1 & defaultNaNF32UI) == defaultNaNF32UI; bool v2_nan = (v2 & defaultNaNF32UI) == defaultNaNF32UI; bool v1_snan = softfloat_isSigNaNF32UI(v1); bool v2_snan = softfloat_isSigNaNF32UI(v2); if(v1_snan || v2_snan) softfloat_raiseFlags(softfloat_flag_invalid); if(v1_nan || v1_snan) return (v2_nan || v2_snan) ? defaultNaNF32UI : v2; else if(v2_nan || v2_snan) return v1; else { if((v1 & 0x7fffffff) == 0 && (v2 & 0x7fffffff) == 0) { return op == 0 ? ((v1 & 0x80000000) ? v1 : v2) : ((v1 & 0x80000000) ? v2 : v1); } else { float32_t v1f{v1}, v2f{v2}; return op == 0 ? (f32_lt(v1f, v2f) ? v1 : v2) : (f32_lt(v1f, v2f) ? v2 : v1); } } } uint32_t fclass_s(uint32_t v1) { float32_t a{v1}; union ui32_f32 uA; uint_fast32_t uiA; uA.f = a; uiA = uA.ui; bool infOrNaN = expF32UI(uiA) == 0xFF; bool subnormalOrZero = expF32UI(uiA) == 0; bool sign = signF32UI(uiA); bool fracZero = fracF32UI(uiA) == 0; bool isNaN = isNaNF32UI(uiA); bool isSNaN = softfloat_isSigNaNF32UI(uiA); return (sign && infOrNaN && fracZero) << 0 | (sign && !infOrNaN && !subnormalOrZero) << 1 | (sign && subnormalOrZero && !fracZero) << 2 | (sign && subnormalOrZero && fracZero) << 3 | (!sign && infOrNaN && fracZero) << 7 | (!sign && !infOrNaN && !subnormalOrZero) << 6 | (!sign && subnormalOrZero && !fracZero) << 5 | (!sign && subnormalOrZero && fracZero) << 4 | (isNaN && isSNaN) << 8 | (isNaN && !isSNaN) << 9; } uint32_t fconv_d2f(uint64_t v1, uint8_t mode) { if(softfloat_isSigNaNF64UI(v1)) { softfloat_raiseFlags(softfloat_flag_invalid); return defaultNaNF32UI; } else { softfloat_roundingMode = rmm_map.at(mode); softfloat_exceptionFlags = 0; float32_t res = f64_to_f32(float64_t{v1}); return res.v; } } uint64_t fconv_f2d(uint32_t v1, uint8_t mode) { bool infOrNaN = expF32UI(v1) == 0xFF; bool subnormalOrZero = expF32UI(v1) == 0; if(infOrNaN || subnormalOrZero) { return defaultNaNF64UI; } else { float64_t res = f32_to_f64(float32_t{v1}); return res.v; } } uint64_t fadd_d(uint64_t v1, uint64_t v2, uint8_t mode) { bool nan = (v1 & defaultNaNF32UI) == quiet_nan32; bool snan = softfloat_isSigNaNF32UI(v1); float64_t v1f{v1}, v2f{v2}; softfloat_roundingMode = rmm_map.at(mode); softfloat_exceptionFlags = 0; float64_t r = f64_add(v1f, v2f); return r.v; } uint64_t fsub_d(uint64_t v1, uint64_t v2, uint8_t mode) { float64_t v1f{v1}, v2f{v2}; softfloat_roundingMode = rmm_map.at(mode); softfloat_exceptionFlags = 0; float64_t r = f64_sub(v1f, v2f); return r.v; } uint64_t fmul_d(uint64_t v1, uint64_t v2, uint8_t mode) { float64_t v1f{v1}, v2f{v2}; softfloat_roundingMode = rmm_map.at(mode); softfloat_exceptionFlags = 0; float64_t r = f64_mul(v1f, v2f); return r.v; } uint64_t fdiv_d(uint64_t v1, uint64_t v2, uint8_t mode) { float64_t v1f{v1}, v2f{v2}; softfloat_roundingMode = rmm_map.at(mode); softfloat_exceptionFlags = 0; float64_t r = f64_div(v1f, v2f); return r.v; } uint64_t fsqrt_d(uint64_t v1, uint8_t mode) { float64_t v1f{v1}; softfloat_roundingMode = rmm_map.at(mode); softfloat_exceptionFlags = 0; float64_t r = f64_sqrt(v1f); return r.v; } uint64_t fcmp_d(uint64_t v1, uint64_t v2, uint32_t op) { float64_t v1f{v1}, v2f{v2}; softfloat_exceptionFlags = 0; bool nan = (v1 & defaultNaNF64UI) == quiet_nan32 || (v2 & defaultNaNF64UI) == quiet_nan32; bool snan = softfloat_isSigNaNF64UI(v1) || softfloat_isSigNaNF64UI(v2); switch(op) { case 0: if(nan | snan) { if(snan) softfloat_raiseFlags(softfloat_flag_invalid); return 0; } else return f64_eq(v1f, v2f) ? 1 : 0; case 1: if(nan | snan) { softfloat_raiseFlags(softfloat_flag_invalid); return 0; } else return f64_le(v1f, v2f) ? 1 : 0; case 2: if(nan | snan) { softfloat_raiseFlags(softfloat_flag_invalid); return 0; } else return f64_lt(v1f, v2f) ? 1 : 0; default: break; } return -1; } uint64_t fcvt_d(uint64_t v1, uint32_t op, uint8_t mode) { float64_t v1f{v1}; softfloat_exceptionFlags = 0; float64_t r; switch(op) { case 0: { // l from d int64_t res = f64_to_i64(v1f, rmm_map.at(mode), true); return (uint64_t)res; } case 1: { // lu from d uint64_t res = f64_to_ui64(v1f, rmm_map.at(mode), true); return res; } case 2: // d from l r = i64_to_f64(v1); return r.v; case 3: // d from lu r = ui64_to_f64(v1); return r.v; } return 0; } uint64_t fmadd_d(uint64_t v1, uint64_t v2, uint64_t v3, uint32_t op, uint8_t mode) { uint64_t F64_SIGN = 1ULL << 63; switch(op) { case 0: // FMADD_D break; case 1: // FMSUB_D v3 ^= F64_SIGN; break; case 2: // FNMADD_D v1 ^= F64_SIGN; v3 ^= F64_SIGN; break; case 3: // FNMSUB_D v1 ^= F64_SIGN; break; } softfloat_roundingMode = rmm_map.at(mode); softfloat_exceptionFlags = 0; float64_t res = softfloat_mulAddF64(v1, v2, v3, 0); return res.v; } uint64_t fsel_d(uint64_t v1, uint64_t v2, uint32_t op) { softfloat_exceptionFlags = 0; bool v1_nan = (v1 & defaultNaNF64UI) == defaultNaNF64UI; bool v2_nan = (v2 & defaultNaNF64UI) == defaultNaNF64UI; bool v1_snan = softfloat_isSigNaNF64UI(v1); bool v2_snan = softfloat_isSigNaNF64UI(v2); if(v1_snan || v2_snan) softfloat_raiseFlags(softfloat_flag_invalid); if(v1_nan || v1_snan) return (v2_nan || v2_snan) ? defaultNaNF64UI : v2; else if(v2_nan || v2_snan) return v1; else { if((v1 & std::numeric_limits::max()) == 0 && (v2 & std::numeric_limits::max()) == 0) { return op == 0 ? ((v1 & std::numeric_limits::min()) ? v1 : v2) : ((v1 & std::numeric_limits::min()) ? v2 : v1); } else { float64_t v1f{v1}, v2f{v2}; return op == 0 ? (f64_lt(v1f, v2f) ? v1 : v2) : (f64_lt(v1f, v2f) ? v2 : v1); } } } uint64_t fclass_d(uint64_t v1) { float64_t a{v1}; union ui64_f64 uA; uint_fast64_t uiA; uA.f = a; uiA = uA.ui; bool infOrNaN = expF64UI(uiA) == 0x7FF; bool subnormalOrZero = expF64UI(uiA) == 0; bool sign = signF64UI(uiA); bool fracZero = fracF64UI(uiA) == 0; bool isNaN = isNaNF64UI(uiA); bool isSNaN = softfloat_isSigNaNF64UI(uiA); return (sign && infOrNaN && fracZero) << 0 | (sign && !infOrNaN && !subnormalOrZero) << 1 | (sign && subnormalOrZero && !fracZero) << 2 | (sign && subnormalOrZero && fracZero) << 3 | (!sign && infOrNaN && fracZero) << 7 | (!sign && !infOrNaN && !subnormalOrZero) << 6 | (!sign && subnormalOrZero && !fracZero) << 5 | (!sign && subnormalOrZero && fracZero) << 4 | (isNaN && isSNaN) << 8 | (isNaN && !isSNaN) << 9; } uint64_t fcvt_32_64(uint32_t v1, uint32_t op, uint8_t mode) { float32_t v1f{v1}; softfloat_exceptionFlags = 0; float64_t r; switch(op) { case 0: // l->s, fp to int32 return f32_to_i64(v1f, rmm_map.at(mode), true); case 1: // wu->s return f32_to_ui64(v1f, rmm_map.at(mode), true); case 2: // s->w r = i32_to_f64(v1); return r.v; case 3: // s->wu r = ui32_to_f64(v1); return r.v; } return 0; } uint32_t fcvt_64_32(uint64_t v1, uint32_t op, uint8_t mode) { softfloat_exceptionFlags = 0; float32_t r; switch(op) { case 0: { // wu->s int32_t r = f64_to_i32(float64_t{v1}, rmm_map.at(mode), true); return r; } case 1: { // wu->s uint32_t r = f64_to_ui32(float64_t{v1}, rmm_map.at(mode), true); return r; } case 2: // l->s, fp to int32 r = i64_to_f32(v1); return r.v; case 3: // wu->s r = ui64_to_f32(v1); return r.v; } return 0; } uint32_t unbox_s(uint64_t v) { constexpr uint64_t mask = std::numeric_limits::max() & ~((uint64_t)std::numeric_limits::max()); if((v & mask) != mask) return 0x7fc00000; else return v & std::numeric_limits::max(); } } template T constexpr defaultNaN(); template <> uint16_t constexpr defaultNaN() { return defaultNaNF16UI; } template <> uint32_t constexpr defaultNaN() { return defaultNaNF32UI; } template <> uint64_t constexpr defaultNaN() { return defaultNaNF64UI; } template T constexpr posInf(); template <> uint16_t constexpr posInf() { return 0x7C00; } template <> uint32_t constexpr posInf() { return 0x7F800000; } template <> uint64_t constexpr posInf() { return 0x7FF0000000000000; } template T constexpr negInf(); template <> uint16_t constexpr negInf() { return 0xFC00; } template <> uint32_t constexpr negInf() { return 0xFF800000; } template <> uint64_t constexpr negInf() { return 0xFFF0000000000000; } template T constexpr negZero(); template <> uint16_t constexpr negZero() { return 0x8000; } template <> uint32_t constexpr negZero() { return 0x80000000; } template <> uint64_t constexpr negZero() { return 0x8000000000000000; } template bool rsqrt_check(T fclass_val, bool& subnormal, T& ret_val) { softfloat_exceptionFlags = 0; switch(fclass_val) { case 0x0001: { softfloat_exceptionFlags |= softfloat_flag_invalid; ret_val = defaultNaN(); return true; } case 0x0002: { softfloat_exceptionFlags |= softfloat_flag_invalid; ret_val = defaultNaN(); return true; } case 0x0004: { softfloat_exceptionFlags |= softfloat_flag_invalid; ret_val = defaultNaN(); return true; } case 0x0100: { softfloat_exceptionFlags |= softfloat_flag_invalid; ret_val = defaultNaN(); return true; } case 0x0200: { ret_val = defaultNaN(); return true; } case 0x0008: { softfloat_exceptionFlags |= softfloat_flag_infinite; ret_val = negInf(); return true; } case 0x0010: { softfloat_exceptionFlags |= softfloat_flag_infinite; ret_val = posInf(); return true; } case 0x0080: { ret_val = 0; return true; } case 0x0020: { subnormal = true; } default: return false; } } static constexpr std::array, 2> rsqrt_table{ {{ 52, 51, 50, 48, 47, 46, 44, 43, 42, 41, 40, 39, 38, 36, 35, 34, 33, 32, 31, 30, 30, 29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 14, 14, 13, 12, 12, 11, 10, 10, 9, 9, 8, 7, 7, 6, 6, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, }, {127, 125, 123, 121, 119, 118, 116, 114, 113, 111, 109, 108, 106, 105, 103, 102, 100, 99, 97, 96, 95, 93, 92, 91, 90, 88, 87, 86, 85, 84, 83, 82, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 70, 69, 68, 67, 66, 65, 64, 63, 63, 62, 61, 60, 59, 59, 58, 57, 56, 56, 55, 54, 53}}}; uint64_t constexpr frsqrt7_general(const unsigned s, const unsigned e, const uint64_t sign, const int64_t exp, const uint64_t sig, const bool subnormal) { int64_t normalized_exp = exp; uint64_t normalized_sig = sig; if(subnormal) { signed nr_leadingzeros = __builtin_clzll(sig) - (64 - s); normalized_exp = -nr_leadingzeros; normalized_sig = (sig << (1 + nr_leadingzeros)) & ((1ULL << s) - 1); } unsigned exp_idx = normalized_exp & 1; unsigned sig_idx = (normalized_sig >> (s - 6)) & 0x3f; // The output of the table becomes the seven high bits of the result significand (after the leading one); the remainder of the // result significand is zero. uint64_t out_sig = rsqrt_table[exp_idx][sig_idx] << (s - 7); // The output exponent equals floor((3*B - 1 - the normalized input exponent) / 2), where B is the exponent bias. unsigned bias = (1UL << (e - 1)) - 1; uint64_t out_exp = (3 * bias - 1 - normalized_exp) / 2; // The output sign equals the input sign. return (sign << (s + e)) | (out_exp << s) | out_sig; } uint16_t frsqrt7_h(uint16_t v) { bool subnormal = false; uint16_t ret_val = 0; if(rsqrt_check(fclass_h(v), subnormal, ret_val)) { return ret_val; } uint16_t sig = fracF64UI(v); int16_t exp = expF64UI(v); uint16_t sign = signF64UI(v); unsigned constexpr e = 5; unsigned constexpr s = 10; return frsqrt7_general(s, e, sign, exp, sig, subnormal); } uint32_t frsqrt7_s(uint32_t v) { bool subnormal = false; uint32_t ret_val = 0; if(rsqrt_check(fclass_s(v), subnormal, ret_val)) { return ret_val; } uint32_t sig = fracF32UI(v); int exp = expF32UI(v); uint32_t sign = signF32UI(v); unsigned constexpr e = 8; unsigned constexpr s = 23; return frsqrt7_general(s, e, sign, exp, sig, subnormal); } uint64_t frsqrt7_d(uint64_t v) { bool subnormal = false; uint64_t ret_val = 0; if(rsqrt_check(fclass_d(v), subnormal, ret_val)) { return ret_val; } uint64_t sig = fracF64UI(v); int exp = expF64UI(v); uint64_t sign = signF64UI(v); unsigned constexpr e = 11; unsigned constexpr s = 52; return frsqrt7_general(s, e, sign, exp, sig, subnormal); } template bool recip_check(T fclass_val, bool& subnormal, uint64_t& ret_val) { softfloat_exceptionFlags = 0; switch(fclass_val) { case 0x0001: { ret_val = negZero(); return true; } case 0x0080: { ret_val = 0; return true; } case 0x0008: { softfloat_exceptionFlags |= softfloat_flag_infinite; ret_val = negInf(); return true; } case 0x0010: { softfloat_exceptionFlags |= softfloat_flag_infinite; ret_val = posInf(); return true; } case 0x0100: { softfloat_exceptionFlags |= softfloat_flag_invalid; ret_val = defaultNaN(); return true; } case 0x0200: { ret_val = defaultNaN(); return true; } case 0x0004: { subnormal = true; return false; } case 0x0020: { subnormal = true; return false; } default: { subnormal = false; return false; } } } static constexpr std::array rec_table{ {127, 125, 123, 121, 119, 117, 116, 114, 112, 110, 109, 107, 105, 104, 102, 100, 99, 97, 96, 94, 93, 91, 90, 88, 87, 85, 84, 83, 81, 80, 79, 77, 76, 75, 74, 72, 71, 70, 69, 68, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 40, 39, 38, 37, 36, 35, 35, 34, 33, 32, 31, 31, 30, 29, 28, 28, 27, 26, 25, 25, 24, 23, 23, 22, 21, 21, 20, 19, 19, 18, 17, 17, 16, 15, 15, 14, 14, 13, 12, 12, 11, 11, 10, 9, 9, 8, 8, 7, 7, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0}}; bool frec_general(uint64_t& res, const unsigned s, const unsigned e, const uint64_t sign, const int64_t exp, const uint64_t sig, const bool subnormal, uint8_t mode) { int nr_leadingzeros = __builtin_clzll(sig) - (64 - s); int64_t normalized_exp = subnormal ? -nr_leadingzeros : exp; uint64_t normalized_sig = subnormal ? ((sig << (1 + nr_leadingzeros)) & ((1ULL << s) - 1)) : sig; unsigned idx = (normalized_sig >> (s - 7)) & 0x7f; unsigned bias = (1UL << (e - 1)) - 1; uint64_t mid_exp = 2 * (bias)-1 - normalized_exp; uint64_t mid_sig = rec_table[idx] << (s - 7); uint64_t out_exp = mid_exp; uint64_t out_sig = mid_sig; if(mid_exp == 0) { out_exp = mid_exp; out_sig = (mid_sig >> 1) | (1ULL << (s - 1)); } else if(mid_exp == (1ULL << e) - 1) { out_exp = 0; out_sig = (mid_sig >> 2) | (1ULL << (s - 2)); } if(subnormal && nr_leadingzeros > 1) { if((mode == 0b001) || (mode == 0b010 && sign == 0b0) || (mode == 0b011 && sign == 0b1)) { res = (sign << (s + e)) | ((1ULL << (e - 1)) - 1) << s | ((1ULL << s) - 1); return true; } else { res = (sign << (s + e)) | ((1ULL << e) - 1) << s; return true; } } res = (sign << (s + e)) | (out_exp << s) | out_sig; return false; } uint16_t frec7_h(uint16_t v, uint8_t mode) { bool subnormal = false; uint64_t ret_val = 0; if(recip_check(fclass_h(v), subnormal, ret_val)) { return ret_val; } uint16_t sig = fracF16UI(v); int exp = expF16UI(v); uint16_t sign = signF16UI(v); unsigned constexpr e = 5; unsigned constexpr s = 10; if(frec_general(ret_val, s, e, sign, exp, sig, subnormal, mode)) softfloat_exceptionFlags |= (softfloat_flag_inexact | softfloat_flag_overflow); return ret_val; } uint32_t frec7_s(uint32_t v, uint8_t mode) { bool subnormal = false; uint64_t ret_val = 0; if(recip_check(fclass_s(v), subnormal, ret_val)) { return ret_val; } uint32_t sig = fracF32UI(v); int exp = expF32UI(v); uint32_t sign = signF32UI(v); unsigned constexpr e = 8; unsigned constexpr s = 23; if(frec_general(ret_val, s, e, sign, exp, sig, subnormal, mode)) softfloat_exceptionFlags |= (softfloat_flag_inexact | softfloat_flag_overflow); return ret_val; } uint64_t frec7_d(uint64_t v, uint8_t mode) { bool subnormal = false; uint64_t ret_val = 0; if(recip_check(fclass_d(v), subnormal, ret_val)) { return ret_val; } uint64_t sig = fracF64UI(v); int exp = expF64UI(v); uint64_t sign = signF64UI(v); unsigned constexpr e = 11; unsigned constexpr s = 52; if(frec_general(ret_val, s, e, sign, exp, sig, subnormal, mode)) softfloat_exceptionFlags |= (softfloat_flag_inexact | softfloat_flag_overflow); return ret_val; }