/* * Ported from a work by Andreas Grabher for Previous, NeXT Computer Emulator, * derived from NetBSD M68040 FPSP functions, * derived from release 2a of the SoftFloat IEC/IEEE Floating-point Arithmetic * Package. Those parts of the code (and some later contributions) are * provided under that license, as detailed below. * It has subsequently been modified by contributors to the QEMU Project, * so some portions are provided under: * the SoftFloat-2a license * the BSD license * GPL-v2-or-later * * Any future contributions to this file will be taken to be licensed under * the Softfloat-2a license unless specifically indicated otherwise. */ /* Portions of this work are licensed under the terms of the GNU GPL, * version 2 or later. See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" #include "softfloat.h" #include "fpu/softfloat-macros.h" static floatx80 propagateFloatx80NaNOneArg(floatx80 a, float_status *status) { if (floatx80_is_signaling_nan(a, status)) { float_raise(float_flag_invalid, status); } if (status->default_nan_mode) { return floatx80_default_nan(status); } return floatx80_maybe_silence_nan(a, status); } /*---------------------------------------------------------------------------- | Returns the modulo remainder of the extended double-precision floating-point | value `a' with respect to the corresponding value `b'. *----------------------------------------------------------------------------*/ floatx80 floatx80_mod(floatx80 a, floatx80 b, float_status *status) { flag aSign, zSign; int32_t aExp, bExp, expDiff; uint64_t aSig0, aSig1, bSig; uint64_t qTemp, term0, term1; aSig0 = extractFloatx80Frac(a); aExp = extractFloatx80Exp(a); aSign = extractFloatx80Sign(a); bSig = extractFloatx80Frac(b); bExp = extractFloatx80Exp(b); if (aExp == 0x7FFF) { if ((uint64_t) (aSig0 << 1) || ((bExp == 0x7FFF) && (uint64_t) (bSig << 1))) { return propagateFloatx80NaN(a, b, status); } goto invalid; } if (bExp == 0x7FFF) { if ((uint64_t) (bSig << 1)) { return propagateFloatx80NaN(a, b, status); } return a; } if (bExp == 0) { if (bSig == 0) { invalid: float_raise(float_flag_invalid, status); return floatx80_default_nan(status); } normalizeFloatx80Subnormal(bSig, &bExp, &bSig); } if (aExp == 0) { if ((uint64_t) (aSig0 << 1) == 0) { return a; } normalizeFloatx80Subnormal(aSig0, &aExp, &aSig0); } bSig |= LIT64(0x8000000000000000); zSign = aSign; expDiff = aExp - bExp; aSig1 = 0; if (expDiff < 0) { return a; } qTemp = (bSig <= aSig0); if (qTemp) { aSig0 -= bSig; } expDiff -= 64; while (0 < expDiff) { qTemp = estimateDiv128To64(aSig0, aSig1, bSig); qTemp = (2 < qTemp) ? qTemp - 2 : 0; mul64To128(bSig, qTemp, &term0, &term1); sub128(aSig0, aSig1, term0, term1, &aSig0, &aSig1); shortShift128Left(aSig0, aSig1, 62, &aSig0, &aSig1); } expDiff += 64; if (0 < expDiff) { qTemp = estimateDiv128To64(aSig0, aSig1, bSig); qTemp = (2 < qTemp) ? qTemp - 2 : 0; qTemp >>= 64 - expDiff; mul64To128(bSig, qTemp << (64 - expDiff), &term0, &term1); sub128(aSig0, aSig1, term0, term1, &aSig0, &aSig1); shortShift128Left(0, bSig, 64 - expDiff, &term0, &term1); while (le128(term0, term1, aSig0, aSig1)) { ++qTemp; sub128(aSig0, aSig1, term0, term1, &aSig0, &aSig1); } } return normalizeRoundAndPackFloatx80( 80, zSign, bExp + expDiff, aSig0, aSig1, status); } /*---------------------------------------------------------------------------- | Returns the mantissa of the extended double-precision floating-point | value `a'. *----------------------------------------------------------------------------*/ floatx80 floatx80_getman(floatx80 a, float_status *status) { flag aSign; int32_t aExp; uint64_t aSig; aSig = extractFloatx80Frac(a); aExp = extractFloatx80Exp(a); aSign = extractFloatx80Sign(a); if (aExp == 0x7FFF) { if ((uint64_t) (aSig << 1)) { return propagateFloatx80NaNOneArg(a , status); } float_raise(float_flag_invalid , status); return floatx80_default_nan(status); } if (aExp == 0) { if (aSig == 0) { return packFloatx80(aSign, 0, 0); } normalizeFloatx80Subnormal(aSig, &aExp, &aSig); } return roundAndPackFloatx80(status->floatx80_rounding_precision, aSign, 0x3FFF, aSig, 0, status); } /*---------------------------------------------------------------------------- | Returns the exponent of the extended double-precision floating-point | value `a' as an extended double-precision value. *----------------------------------------------------------------------------*/ floatx80 floatx80_getexp(floatx80 a, float_status *status) { flag aSign; int32_t aExp; uint64_t aSig; aSig = extractFloatx80Frac(a); aExp = extractFloatx80Exp(a); aSign = extractFloatx80Sign(a); if (aExp == 0x7FFF) { if ((uint64_t) (aSig << 1)) { return propagateFloatx80NaNOneArg(a , status); } float_raise(float_flag_invalid , status); return floatx80_default_nan(status); } if (aExp == 0) { if (aSig == 0) { return packFloatx80(aSign, 0, 0); } normalizeFloatx80Subnormal(aSig, &aExp, &aSig); } return int32_to_floatx80(aExp - 0x3FFF, status); } /*---------------------------------------------------------------------------- | Scales extended double-precision floating-point value in operand `a' by | value `b'. The function truncates the value in the second operand 'b' to | an integral value and adds that value to the exponent of the operand 'a'. | The operation performed according to the IEC/IEEE Standard for Binary | Floating-Point Arithmetic. *----------------------------------------------------------------------------*/ floatx80 floatx80_scale(floatx80 a, floatx80 b, float_status *status) { flag aSign, bSign; int32_t aExp, bExp, shiftCount; uint64_t aSig, bSig; aSig = extractFloatx80Frac(a); aExp = extractFloatx80Exp(a); aSign = extractFloatx80Sign(a); bSig = extractFloatx80Frac(b); bExp = extractFloatx80Exp(b); bSign = extractFloatx80Sign(b); if (bExp == 0x7FFF) { if ((uint64_t) (bSig << 1) || ((aExp == 0x7FFF) && (uint64_t) (aSig << 1))) { return propagateFloatx80NaN(a, b, status); } float_raise(float_flag_invalid , status); return floatx80_default_nan(status); } if (aExp == 0x7FFF) { if ((uint64_t) (aSig << 1)) { return propagateFloatx80NaN(a, b, status); } return packFloatx80(aSign, floatx80_infinity.high, floatx80_infinity.low); } if (aExp == 0) { if (aSig == 0) { return packFloatx80(aSign, 0, 0); } if (bExp < 0x3FFF) { return a; } normalizeFloatx80Subnormal(aSig, &aExp, &aSig); } if (bExp < 0x3FFF) { return a; } if (0x400F < bExp) { aExp = bSign ? -0x6001 : 0xE000; return roundAndPackFloatx80(status->floatx80_rounding_precision, aSign, aExp, aSig, 0, status); } shiftCount = 0x403E - bExp; bSig >>= shiftCount; aExp = bSign ? (aExp - bSig) : (aExp + bSig); return roundAndPackFloatx80(status->floatx80_rounding_precision, aSign, aExp, aSig, 0, status); }