aboutsummaryrefslogtreecommitdiff
path: root/softfloat/s_mulAddF64.c
diff options
context:
space:
mode:
Diffstat (limited to 'softfloat/s_mulAddF64.c')
-rwxr-xr-xsoftfloat/s_mulAddF64.c188
1 files changed, 188 insertions, 0 deletions
diff --git a/softfloat/s_mulAddF64.c b/softfloat/s_mulAddF64.c
new file mode 100755
index 0000000..01ba3b4
--- /dev/null
+++ b/softfloat/s_mulAddF64.c
@@ -0,0 +1,188 @@
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "platform.h"
+#include "primitives.h"
+#include "internals.h"
+#include "specialize.h"
+#include "softfloat.h"
+
+float64_t
+ softfloat_mulAddF64(
+ int op, uint_fast64_t uiA, uint_fast64_t uiB, uint_fast64_t uiC )
+{
+ bool signA;
+ int_fast16_t expA;
+ uint_fast64_t sigA;
+ bool signB;
+ int_fast16_t expB;
+ uint_fast64_t sigB;
+ bool signC;
+ int_fast16_t expC;
+ uint_fast64_t sigC;
+ bool signProd;
+ uint_fast64_t magBits, uiZ;
+ struct exp16_sig64 normExpSig;
+ int_fast16_t expProd;
+ struct uint128 sigProd;
+ bool signZ;
+ int_fast16_t expZ;
+ uint_fast64_t sigZ;
+ int_fast16_t expDiff;
+ struct uint128 sigC128, sigZ128;
+ int shiftCount;
+ union ui64_f64 uZ;
+
+ signA = signF64UI( uiA );
+ expA = expF64UI( uiA );
+ sigA = fracF64UI( uiA );
+ signB = signF64UI( uiB );
+ expB = expF64UI( uiB );
+ sigB = fracF64UI( uiB );
+ signC = signF64UI( uiC ) ^ ( op == softfloat_mulAdd_subC );
+ expC = expF64UI( uiC );
+ sigC = fracF64UI( uiC );
+ signProd = signA ^ signB ^ ( op == softfloat_mulAdd_subProd );
+ if ( expA == 0x7FF ) {
+ if ( sigA || ( ( expB == 0x7FF ) && sigB ) ) goto propagateNaN_ABC;
+ magBits = expB | sigB;
+ goto infProdArg;
+ }
+ if ( expB == 0x7FF ) {
+ if ( sigB ) goto propagateNaN_ABC;
+ magBits = expA | sigA;
+ goto infProdArg;
+ }
+ if ( expC == 0x7FF ) {
+ if ( sigC ) {
+ uiZ = 0;
+ goto propagateNaN_ZC;
+ }
+ uiZ = uiC;
+ goto uiZ;
+ }
+ if ( ! expA ) {
+ if ( ! sigA ) goto zeroProd;
+ normExpSig = softfloat_normSubnormalF64Sig( sigA );
+ expA = normExpSig.exp;
+ sigA = normExpSig.sig;
+ }
+ if ( ! expB ) {
+ if ( ! sigB ) goto zeroProd;
+ normExpSig = softfloat_normSubnormalF64Sig( sigB );
+ expB = normExpSig.exp;
+ sigB = normExpSig.sig;
+ }
+ expProd = expA + expB - 0x3FE;
+ sigA = ( sigA | UINT64_C( 0x0010000000000000 ) )<<10;
+ sigB = ( sigB | UINT64_C( 0x0010000000000000 ) )<<10;
+ sigProd = softfloat_mul64To128( sigA, sigB );
+ if ( sigProd.v64 < UINT64_C( 0x2000000000000000 ) ) {
+ --expProd;
+ sigProd = softfloat_shortShift128Left( sigProd.v64, sigProd.v0, 1 );
+ }
+ signZ = signProd;
+ if ( ! expC ) {
+ if ( ! sigC ) {
+ expZ = expProd - 1;
+ sigZ = sigProd.v64<<1 | ( sigProd.v0 != 0 );
+ goto roundPack;
+ }
+ normExpSig = softfloat_normSubnormalF64Sig( sigC );
+ expC = normExpSig.exp;
+ sigC = normExpSig.sig;
+ }
+ sigC = ( sigC | UINT64_C( 0x0010000000000000 ) )<<9;
+ expDiff = expProd - expC;
+ if ( signProd == signC ) {
+ if ( expDiff <= 0 ) {
+ expZ = expC;
+ if ( expDiff ) {
+ sigProd.v64 =
+ softfloat_shift64RightJam( sigProd.v64, - expDiff );
+ }
+ sigZ = ( sigC + sigProd.v64 ) | ( sigProd.v0 != 0 );
+ } else {
+ expZ = expProd;
+ sigC128 = softfloat_shift128RightJam( sigC, 0, expDiff );
+ sigZ128 =
+ softfloat_add128(
+ sigProd.v64, sigProd.v0, sigC128.v64, sigC128.v0 );
+ sigZ = sigZ128.v64 | ( sigZ128.v0 != 0 );
+ }
+ if ( sigZ < UINT64_C( 0x4000000000000000 ) ) {
+ --expZ;
+ sigZ <<= 1;
+ }
+ } else {
+/*** OPTIMIZE BETTER? ***/
+ if ( expDiff < 0 ) {
+ signZ = signC;
+ expZ = expC;
+ sigProd =
+ softfloat_shift128RightJam(
+ sigProd.v64, sigProd.v0, - expDiff );
+ sigZ128 = softfloat_sub128( sigC, 0, sigProd.v64, sigProd.v0 );
+ } else if ( ! expDiff ) {
+ expZ = expProd;
+ sigZ128 = softfloat_sub128( sigProd.v64, sigProd.v0, sigC, 0 );
+ if ( ! ( sigZ128.v64 | sigZ128.v0 ) ) goto completeCancellation;
+ if ( sigZ128.v64 & UINT64_C( 0x8000000000000000 ) ) {
+ signZ ^= 1;
+ sigZ128 = softfloat_sub128( 0, 0, sigZ128.v64, sigZ128.v0 );
+ }
+ } else {
+ expZ = expProd;
+ sigC128 = softfloat_shift128RightJam( sigC, 0, expDiff );
+ sigZ128 =
+ softfloat_sub128(
+ sigProd.v64, sigProd.v0, sigC128.v64, sigC128.v0 );
+ }
+ if ( ! sigZ128.v64 ) {
+ expZ -= 64;
+ sigZ128.v64 = sigZ128.v0;
+ sigZ128.v0 = 0;
+ }
+ shiftCount = softfloat_countLeadingZeros64( sigZ128.v64 ) - 1;
+ expZ -= shiftCount;
+ if ( shiftCount < 0 ) {
+ sigZ = softfloat_shortShift64RightJam( sigZ128.v64, - shiftCount );
+ } else {
+ sigZ128 =
+ softfloat_shortShift128Left(
+ sigZ128.v64, sigZ128.v0, shiftCount );
+ sigZ = sigZ128.v64;
+ }
+ sigZ |= ( sigZ128.v0 != 0 );
+ }
+ roundPack:
+ return softfloat_roundPackToF64( signZ, expZ, sigZ );
+ propagateNaN_ABC:
+ uiZ = softfloat_propagateNaNF64UI( uiA, uiB );
+ goto propagateNaN_ZC;
+ infProdArg:
+ if ( magBits ) {
+ uiZ = packToF64UI( signProd, 0x7FF, 0 );
+ if ( expC != 0x7FF ) goto uiZ;
+ if ( sigC ) goto propagateNaN_ZC;
+ if ( signProd == signC ) goto uiZ;
+ }
+ invalid:
+ softfloat_raiseFlags( softfloat_flag_invalid );
+ uiZ = defaultNaNF64UI;
+ propagateNaN_ZC:
+ uiZ = softfloat_propagateNaNF64UI( uiZ, uiC );
+ goto uiZ;
+ zeroProd:
+ uiZ = uiC;
+ if ( ! ( expC | sigC ) && ( signProd != signC ) ) {
+ completeCancellation:
+ uiZ =
+ packToF64UI( softfloat_roundingMode == softfloat_round_min, 0, 0 );
+ }
+ uiZ:
+ uZ.ui = uiZ;
+ return uZ.f;
+
+}
+