| /* |
| * 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); |
| } |