
/*
===============================================================================

This C source file is part of TestFloat, Release 2a, a package of programs
for testing the correctness of floating-point arithmetic complying to the
IEC/IEEE Standard for Floating-Point.

Written by John R. Hauser.  More information is available through the Web
page `http://HTTP.CS.Berkeley.EDU/~jhauser/arithmetic/TestFloat.html'.

THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort
has been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT
TIMES RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO
PERSONS AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ANY
AND ALL LOSSES, COSTS, OR OTHER PROBLEMS ARISING FROM ITS USE.

Derivative works are acceptable, even for commercial purposes, so long as
(1) they include prominent notice that the work is derivative, and (2) they
include prominent notice akin to these four paragraphs for those parts of
this code that are retained.

Modified for use with or1ksim's testsuite.

Contributor Julius Baxter <julius.baxter@orsoc.se>

===============================================================================
*/

#include "milieu.h"
#include "softfloat.h"
#include "slowfloat.h"

// From slowfloat-64.c

int8 slow_float_rounding_mode;
int8 slow_float_exception_flags;
int8 slow_float_detect_tininess;
#ifdef FLOATX80
int8 slow_floatx80_rounding_precision;
#endif

typedef struct {
    bits64 a0, a1;
} bits128X;

typedef struct {
    flag isNaN;
    flag isInf;
    flag isZero;
    flag sign;
    int32 exp;
    bits128X sig;
} floatX;

static const floatX floatXNaN = { TRUE, FALSE, FALSE, FALSE, 0, { 0, 0 } };
static const floatX floatXPositiveZero =
    { FALSE, FALSE, TRUE, FALSE, 0, { 0, 0 } };
static const floatX floatXNegativeZero =
    { FALSE, FALSE, TRUE, TRUE, 0, { 0, 0 } };

static bits128X shortShift128Left( bits128X a, int8 shiftCount )
{
    int8 negShiftCount;

    negShiftCount = ( - shiftCount & 63 );
    a.a0 = ( a.a0<<shiftCount ) | ( a.a1>>negShiftCount );
    a.a1 <<= shiftCount;
    return a;

}

static bits128X shortShift128RightJamming( bits128X a, int8 shiftCount )
{
    int8 negShiftCount;
    bits64 extra;

    negShiftCount = ( - shiftCount & 63 );
    extra = a.a1<<negShiftCount;
    a.a1 = ( a.a0<<negShiftCount ) | ( a.a1>>shiftCount ) | ( extra != 0 );
    a.a0 >>= shiftCount;
    return a;

}

static bits128X neg128( bits128X a )
{

    if ( a.a1 == 0 ) {
        a.a0 = - a.a0;
    }
    else {
        a.a1 = - a.a1;
        a.a0 = ~ a.a0;
    }
    return a;

}

static bits128X add128( bits128X a, bits128X b )
{

    a.a1 += b.a1;
    a.a0 += b.a0 + ( a.a1 < b.a1 );
    return a;

}

static flag eq128( bits128X a, bits128X b )
{

    return ( a.a0 == b.a0 ) && ( a.a1 == b.a1 );

}

static flag le128( bits128X a, bits128X b )
{

    return ( a.a0 < b.a0 ) || ( ( a.a0 == b.a0 ) && ( a.a1 <= b.a1 ) );

}

static flag lt128( bits128X a, bits128X b )
{

    return ( a.a0 < b.a0 ) || ( ( a.a0 == b.a0 ) && ( a.a1 < b.a1 ) );

}

static floatX roundFloatXTo24( flag isTiny, floatX zx )
{
    bits32 roundBits;

    zx.sig.a0 |= ( zx.sig.a1 != 0 );
    zx.sig.a1 = 0;
    roundBits = zx.sig.a0 & 0xFFFFFFFF;
    zx.sig.a0 -= roundBits;
    if ( roundBits ) {
        slow_float_exception_flags |= float_flag_inexact;
        if ( isTiny ) slow_float_exception_flags |= float_flag_underflow;
        switch ( slow_float_rounding_mode ) {
         case float_round_nearest_even:
            if ( roundBits < 0x80000000 ) goto noIncrement;
            if (    ( roundBits == 0x80000000 )
                 && ! ( zx.sig.a0 & LIT64( 0x100000000 ) ) ) {
                goto noIncrement;
            }
            break;
         case float_round_to_zero:
            goto noIncrement;
         case float_round_down:
            if ( ! zx.sign ) goto noIncrement;
            break;
         case float_round_up:
            if ( zx.sign ) goto noIncrement;
            break;
        }
        zx.sig.a0 += LIT64( 0x100000000 );
        if ( zx.sig.a0 == LIT64( 0x0100000000000000 ) ) {
            zx.sig.a0 = LIT64( 0x0080000000000000 );
            ++zx.exp;
        }
    }
 noIncrement:
    return zx;

}

static floatX roundFloatXTo53( flag isTiny, floatX zx )
{
    int8 roundBits;

    zx.sig.a0 |= ( zx.sig.a1 != 0 );
    zx.sig.a1 = 0;
    roundBits = zx.sig.a0 & 7;
    zx.sig.a0 -= roundBits;
    if ( roundBits ) {
        slow_float_exception_flags |= float_flag_inexact;
        if ( isTiny ) slow_float_exception_flags |= float_flag_underflow;
        switch ( slow_float_rounding_mode ) {
         case float_round_nearest_even:
            if ( roundBits < 4 ) goto noIncrement;
            if ( ( roundBits == 4 ) && ! ( zx.sig.a0 & 8 ) ) goto noIncrement;
            break;
         case float_round_to_zero:
            goto noIncrement;
         case float_round_down:
            if ( ! zx.sign ) goto noIncrement;
            break;
         case float_round_up:
            if ( zx.sign ) goto noIncrement;
            break;
        }
        zx.sig.a0 += 8;
        if ( zx.sig.a0 == LIT64( 0x0100000000000000 ) ) {
            zx.sig.a0 = LIT64( 0x0080000000000000 );
            ++zx.exp;
        }
    }
 noIncrement:
    return zx;

}

#ifdef FLOATX80
static floatX roundFloatXTo64( flag isTiny, floatX zx )
{
    int64 roundBits;

    roundBits = zx.sig.a1 & LIT64( 0x00FFFFFFFFFFFFFF );
    zx.sig.a1 -= roundBits;
    if ( roundBits ) {
        slow_float_exception_flags |= float_flag_inexact;
        if ( isTiny ) slow_float_exception_flags |= float_flag_underflow;
        switch ( slow_float_rounding_mode ) {
         case float_round_nearest_even:
            if ( roundBits < LIT64( 0x0080000000000000 ) ) goto noIncrement;
            if (    ( roundBits == LIT64( 0x0080000000000000 ) )
                 && ! ( zx.sig.a1 & LIT64( 0x0100000000000000 ) ) ) {
                goto noIncrement;
            }
            break;
         case float_round_to_zero:
            goto noIncrement;
         case float_round_down:
            if ( ! zx.sign ) goto noIncrement;
            break;
         case float_round_up:
            if ( zx.sign ) goto noIncrement;
            break;
        }
        zx.sig.a1 += LIT64( 0x0100000000000000 );
        zx.sig.a0 += ( zx.sig.a1 == 0 );
        if ( zx.sig.a0 == LIT64( 0x0100000000000000 ) ) {
            zx.sig.a0 = LIT64( 0x0080000000000000 );
            ++zx.exp;
        }
    }
 noIncrement:
    return zx;

}
#endif
static floatX roundFloatXTo113( flag isTiny, floatX zx )
{
    int8 roundBits;

    roundBits = zx.sig.a1 & 0x7F;
    zx.sig.a1 -= roundBits;
    if ( roundBits ) {
        slow_float_exception_flags |= float_flag_inexact;
        if ( isTiny ) slow_float_exception_flags |= float_flag_underflow;
        switch ( slow_float_rounding_mode ) {
         case float_round_nearest_even:
            if ( roundBits < 0x40 ) goto noIncrement;
            if (    ( roundBits == 0x40 )
                 && ! ( zx.sig.a1 & 0x80 ) ) goto noIncrement;
            break;
         case float_round_to_zero:
            goto noIncrement;
         case float_round_down:
            if ( ! zx.sign ) goto noIncrement;
            break;
         case float_round_up:
            if ( zx.sign ) goto noIncrement;
            break;
        }
        zx.sig.a1 += 0x80;
        zx.sig.a0 += ( zx.sig.a1 == 0 );
        if ( zx.sig.a0 == LIT64( 0x0100000000000000 ) ) {
            zx.sig.a0 = LIT64( 0x0080000000000000 );
            ++zx.exp;
        }
    }
 noIncrement:
    return zx;

}

static floatX int32ToFloatX( int32 a )
{
    floatX ax;

    ax.isNaN = FALSE;
    ax.isInf = FALSE;
    ax.exp = 0; // Initialise it to keep GCC happy
    ax.sign = ( a < 0 );
    ax.sig.a1 = 0;
    ax.sig.a0 = ax.sign ? - (bits64) a : a;
    if ( a == 0 ) {
        ax.isZero = TRUE;
        return ax;
    }
    ax.isZero = FALSE;
    ax.sig.a0 <<= 24;
    ax.exp = 31;
    while ( ax.sig.a0 < LIT64( 0x0080000000000000 ) ) {
        ax.sig.a0 <<= 1;
        --ax.exp;
    }
    return ax;

}

static int32 floatXToInt32( floatX ax )
{
    int8 savedExceptionFlags;
    int32 shiftCount;
    int32 z;

    if ( ax.isInf || ax.isNaN ) {
        slow_float_exception_flags |= float_flag_invalid;
        return ( ax.isInf & ax.sign ) ? (sbits32) 0x80000000 : 0x7FFFFFFF;
    }
    if ( ax.isZero ) return 0;
    savedExceptionFlags = slow_float_exception_flags;
    shiftCount = 52 - ax.exp;
    if ( 56 < shiftCount ) {
        ax.sig.a1 = 1;
        ax.sig.a0 = 0;
    }
    else {
        while ( 0 < shiftCount ) {
            ax.sig = shortShift128RightJamming( ax.sig, 1 );
            --shiftCount;
        }
    }
    ax = roundFloatXTo53( FALSE, ax );
    ax.sig = shortShift128RightJamming( ax.sig, 3 );
    z = ax.sig.a0;
    if ( ax.sign ) z = - z;
    if (    ( shiftCount < 0 )
         || ( ax.sig.a0>>32 )
         || ( ( z != 0 ) && ( ( ax.sign ^ ( z < 0 ) ) != 0 ) )
       ) {
        slow_float_exception_flags = savedExceptionFlags | float_flag_invalid;
        return ax.sign ? (sbits32) 0x80000000 : 0x7FFFFFFF;
    }
    return z;

}

static floatX int64ToFloatX( int64 a )
{
    floatX ax;

    ax.isNaN = FALSE;
    ax.isInf = FALSE;
    ax.exp = 0; // Init to keep GCC happy
    ax.sign = ( a < 0 );
    ax.sig.a1 = ax.sign ? - a : a;
    ax.sig.a0 = 0;
    if ( a == 0 ) {
        ax.isZero = TRUE;
        return ax;
    }
    ax.isZero = FALSE;
    ax.sig = shortShift128Left( ax.sig, 56 );
    ax.exp = 63;
    while ( ax.sig.a0 < LIT64( 0x0080000000000000 ) ) {
        ax.sig = shortShift128Left( ax.sig, 1 );
        --ax.exp;
    }
    return ax;

}

static int64 floatXToInt64( floatX ax )
{
    int8 savedExceptionFlags;
    int32 shiftCount;
    int64 z;

    if ( ax.isInf || ax.isNaN ) {
        slow_float_exception_flags |= float_flag_invalid;
        return
              ( ax.isInf & ax.sign ) ? (sbits64) LIT64( 0x8000000000000000 )
            : LIT64( 0x7FFFFFFFFFFFFFFF );
    }
    if ( ax.isZero ) return 0;
    savedExceptionFlags = slow_float_exception_flags;
    shiftCount = 112 - ax.exp;
    if ( 116 < shiftCount ) {
        ax.sig.a1 = 1;
        ax.sig.a0 = 0;
    }
    else {
        while ( 0 < shiftCount ) {
            ax.sig = shortShift128RightJamming( ax.sig, 1 );
            --shiftCount;
        }
    }
    ax = roundFloatXTo113( FALSE, ax );
    ax.sig = shortShift128RightJamming( ax.sig, 7 );
    z = ax.sig.a1;
    if ( ax.sign ) z = - z;
    if (    ( shiftCount < 0 )
         || ax.sig.a0
         || ( ( z != 0 ) && ( ( ax.sign ^ ( z < 0 ) ) != 0 ) )
       ) {
        slow_float_exception_flags = savedExceptionFlags | float_flag_invalid;
        return
              ax.sign ? (sbits64) LIT64( 0x8000000000000000 )
            : LIT64( 0x7FFFFFFFFFFFFFFF );
    }
    return z;

}

static floatX float32ToFloatX( float32 a )
{
    int16 expField;
    floatX ax;

    ax.isNaN = FALSE;
    ax.isInf = FALSE;
    ax.isZero = FALSE;
    ax.exp = 0; // Init here to keep GCC happy
    ax.sign = ( ( a & 0x80000000 ) != 0 );
    expField = ( a>>23 ) & 0xFF;
    ax.sig.a1 = 0;
    ax.sig.a0 = a & 0x007FFFFF;
    ax.sig.a0 <<= 32;
    if ( expField == 0 ) {
        if ( ax.sig.a0 == 0 ) {
            ax.isZero = TRUE;
        }
        else {
            expField = 1 - 0x7F;
            do {
                ax.sig.a0 <<= 1;
                --expField;
            } while ( ax.sig.a0 < LIT64( 0x0080000000000000 ) );
            ax.exp = expField;
        }
    }
    else if ( expField == 0xFF ) {
        if ( ax.sig.a0 == 0 ) {
            ax.isInf = TRUE;
        }
        else {
            ax.isNaN = TRUE;
        }
    }
    else {
        ax.sig.a0 |= LIT64( 0x0080000000000000 );
        ax.exp = expField - 0x7F;
    }
    return ax;

}

static float32 floatXToFloat32( floatX zx )
{
    floatX savedZ;
    flag isTiny;
    int32 expField;
    float32 z = 0;// Init this to keep GCC happy.

    if ( zx.isZero ) return zx.sign ? 0x80000000 : 0;
    if ( zx.isInf ) return zx.sign ? 0xFF800000 : 0x7F800000;
    if ( zx.isNaN ) return 0xFFFFFFFF;
    while ( LIT64( 0x0100000000000000 ) <= zx.sig.a0 ) {
        zx.sig = shortShift128RightJamming( zx.sig, 1 );
        ++zx.exp;
    }
    while ( zx.sig.a0 < LIT64( 0x0080000000000000 ) ) {
        zx.sig = shortShift128Left( zx.sig, 1 );
        --zx.exp;
    }
    savedZ = zx;
    isTiny =
           ( slow_float_detect_tininess == float_tininess_before_rounding )
        && ( zx.exp + 0x7F <= 0 );
    zx = roundFloatXTo24( isTiny, zx );
    expField = zx.exp + 0x7F;
    if ( 0xFF <= expField ) {
        slow_float_exception_flags |=
            float_flag_overflow | float_flag_inexact;
        if ( zx.sign ) {
            switch ( slow_float_rounding_mode ) {
             case float_round_nearest_even:
             case float_round_down:
                z = 0xFF800000;
                break;
             case float_round_to_zero:
             case float_round_up:
                z = 0xFF7FFFFF;
                break;
            }
        }
        else {
            switch ( slow_float_rounding_mode ) {
             case float_round_nearest_even:
             case float_round_up:
                z = 0x7F800000;
                break;
             case float_round_to_zero:
             case float_round_down:
                z = 0x7F7FFFFF;
                break;
            }
        }
        return z;
    }
    if ( expField <= 0 ) {
        isTiny = TRUE;
        zx = savedZ;
        expField = zx.exp + 0x7F;
        if ( expField < -27 ) {
            zx.sig.a1 = ( zx.sig.a0 != 0 ) || ( zx.sig.a1 != 0 );
            zx.sig.a0 = 0;
        }
        else {
            while ( expField <= 0 ) {
                zx.sig = shortShift128RightJamming( zx.sig, 1 );
                ++expField;
            }
        }
        zx = roundFloatXTo24( isTiny, zx );
        expField = ( LIT64( 0x0080000000000000 ) <= zx.sig.a0 ) ? 1 : 0;
    }
    z = expField;
    z <<= 23;
    if ( zx.sign ) z |= 0x80000000;
    z |= ( zx.sig.a0>>32 ) & 0x007FFFFF;
    return z;

}

static floatX float64ToFloatX( float64 a )
{
    int16 expField;
    floatX ax;

    ax.isNaN = FALSE;
    ax.isInf = FALSE;
    ax.isZero = FALSE;
    ax.exp = 0 ; // Init to keep GCC happy
    ax.sign = ( ( a & LIT64( 0x8000000000000000 ) ) != 0 );
    expField = ( a>>52 ) & 0x7FF;
    ax.sig.a1 = 0;
    ax.sig.a0 = a & LIT64( 0x000FFFFFFFFFFFFF );
    if ( expField == 0 ) {
        if ( ax.sig.a0 == 0 ) {
            ax.isZero = TRUE;
        }
        else {
            expField = 1 - 0x3FF;
            do {
                ax.sig.a0 <<= 1;
                --expField;
            } while ( ax.sig.a0 < LIT64( 0x0010000000000000 ) );
            ax.exp = expField;
        }
    }
    else if ( expField == 0x7FF ) {
        if ( ax.sig.a0 == 0 ) {
            ax.isInf = TRUE;
        }
        else {
            ax.isNaN = TRUE;
        }
    }
    else {
        ax.exp = expField - 0x3FF;
        ax.sig.a0 |= LIT64( 0x0010000000000000 );
    }
    ax.sig.a0 <<= 3;
    return ax;

}

static float64 floatXToFloat64( floatX zx )
{
    floatX savedZ;
    flag isTiny;
    int32 expField;
    float64 z = 0 ;// Init to keep GCC happy

    if ( zx.isZero ) return zx.sign ? LIT64( 0x8000000000000000 ) : 0;
    if ( zx.isInf ) {
        return
              zx.sign ? LIT64( 0xFFF0000000000000 )
            : LIT64( 0x7FF0000000000000 );
    }
    if ( zx.isNaN ) return LIT64( 0xFFFFFFFFFFFFFFFF );
    while ( LIT64( 0x0100000000000000 ) <= zx.sig.a0 ) {
        zx.sig = shortShift128RightJamming( zx.sig, 1 );
        ++zx.exp;
    }
    while ( zx.sig.a0 < LIT64( 0x0080000000000000 ) ) {
        zx.sig = shortShift128Left( zx.sig, 1 );
        --zx.exp;
    }
    savedZ = zx;
    isTiny =
           ( slow_float_detect_tininess == float_tininess_before_rounding )
        && ( zx.exp + 0x3FF <= 0 );
    zx = roundFloatXTo53( isTiny, zx );
    expField = zx.exp + 0x3FF;
    if ( 0x7FF <= expField ) {
        slow_float_exception_flags |=
            float_flag_overflow | float_flag_inexact;
        if ( zx.sign ) {
            switch ( slow_float_rounding_mode ) {
             case float_round_nearest_even:
             case float_round_down:
                z = LIT64( 0xFFF0000000000000 );
                break;
             case float_round_to_zero:
             case float_round_up:
                z = LIT64( 0xFFEFFFFFFFFFFFFF );
                break;
            }
        }
        else {
            switch ( slow_float_rounding_mode ) {
             case float_round_nearest_even:
             case float_round_up:
                z = LIT64( 0x7FF0000000000000 );
                break;
             case float_round_to_zero:
             case float_round_down:
                z = LIT64( 0x7FEFFFFFFFFFFFFF );
                break;
            }
        }
        return z;
    }
    if ( expField <= 0 ) {
        isTiny = TRUE;
        zx = savedZ;
        expField = zx.exp + 0x3FF;
        if ( expField < -56 ) {
            zx.sig.a1 = ( zx.sig.a0 != 0 ) || ( zx.sig.a1 != 0 );
            zx.sig.a0 = 0;
        }
        else {
            while ( expField <= 0 ) {
                zx.sig = shortShift128RightJamming( zx.sig, 1 );
                ++expField;
            }
        }
        zx = roundFloatXTo53( isTiny, zx );
        expField = ( LIT64( 0x0080000000000000 ) <= zx.sig.a0 ) ? 1 : 0;
    }
    zx.sig.a0 >>= 3;
    z = expField;
    z <<= 52;
    if ( zx.sign ) z |= LIT64( 0x8000000000000000 );
    z |= zx.sig.a0 & LIT64( 0x000FFFFFFFFFFFFF );
    return z;

}

#ifdef FLOATX80

static floatX floatx80ToFloatX( floatx80 a )
{
    int32 expField;
    floatX ax;

    ax.isNaN = FALSE;
    ax.isInf = FALSE;
    ax.isZero = FALSE;
    ax.sign = ( ( a.high & 0x8000 ) != 0 );
    expField = a.high & 0x7FFF;
    ax.sig.a1 = a.low;
    ax.sig.a0 = 0;
    if ( expField == 0 ) {
        if ( ax.sig.a1 == 0 ) {
            ax.isZero = TRUE;
        }
        else {
            expField = 1 - 0x3FFF;
            while ( ax.sig.a1 < LIT64( 0x8000000000000000 ) ) {
                ax.sig.a1 <<= 1;
                --expField;
            }
            ax.exp = expField;
        }
    }
    else if ( expField == 0x7FFF ) {
        if ( ( ax.sig.a1 & LIT64( 0x7FFFFFFFFFFFFFFF ) ) == 0 ) {
            ax.isInf = TRUE;
        }
        else {
            ax.isNaN = TRUE;
        }
    }
    else {
        ax.exp = expField - 0x3FFF;
    }
    ax.sig = shortShift128Left( ax.sig, 56 );
    return ax;

}

static floatx80 floatXToFloatx80( floatX zx )
{
    floatX savedZ;
    flag isTiny;
    int32 expField;
    floatx80 z;

    if ( zx.isZero ) {
        z.low = 0;
        z.high = zx.sign ? 0x8000 : 0;
        return z;
    }
    if ( zx.isInf ) {
        z.low = LIT64( 0x8000000000000000 );
        z.high = zx.sign ? 0xFFFF : 0x7FFF;
        return z;
    }
    if ( zx.isNaN ) {
        z.low = LIT64( 0xFFFFFFFFFFFFFFFF );
        z.high = 0xFFFF;
        return z;
    }
    while ( LIT64( 0x0100000000000000 ) <= zx.sig.a0 ) {
        zx.sig = shortShift128RightJamming( zx.sig, 1 );
        ++zx.exp;
    }
    while ( zx.sig.a0 < LIT64( 0x0080000000000000 ) ) {
        zx.sig = shortShift128Left( zx.sig, 1 );
        --zx.exp;
    }
    savedZ = zx;
    isTiny =
           ( slow_float_detect_tininess == float_tininess_before_rounding )
        && ( zx.exp + 0x3FFF <= 0 );
    switch ( slow_floatx80_rounding_precision ) {
     case 32:
        zx = roundFloatXTo24( isTiny, zx );
        break;
     case 64:
        zx = roundFloatXTo53( isTiny, zx );
        break;
     default:
        zx = roundFloatXTo64( isTiny, zx );
        break;
    }
    expField = zx.exp + 0x3FFF;
    if ( 0x7FFF <= expField ) {
        slow_float_exception_flags |=
            float_flag_overflow | float_flag_inexact;
        if ( zx.sign ) {
            switch ( slow_float_rounding_mode ) {
             case float_round_nearest_even:
             case float_round_down:
                z.low = LIT64( 0x8000000000000000 );
                z.high = 0xFFFF;
                break;
             case float_round_to_zero:
             case float_round_up:
                switch ( slow_floatx80_rounding_precision ) {
                 case 32:
                    z.low = LIT64( 0xFFFFFF0000000000 );
                    break;
                 case 64:
                    z.low = LIT64( 0xFFFFFFFFFFFFF800 );
                    break;
                 default:
                    z.low = LIT64( 0xFFFFFFFFFFFFFFFF );
                    break;
                }
                z.high = 0xFFFE;
                break;
            }
        }
        else {
            switch ( slow_float_rounding_mode ) {
             case float_round_nearest_even:
             case float_round_up:
                z.low = LIT64( 0x8000000000000000 );
                z.high = 0x7FFF;
                break;
             case float_round_to_zero:
             case float_round_down:
                switch ( slow_floatx80_rounding_precision ) {
                 case 32:
                    z.low = LIT64( 0xFFFFFF0000000000 );
                    break;
                 case 64:
                    z.low = LIT64( 0xFFFFFFFFFFFFF800 );
                    break;
                 default:
                    z.low = LIT64( 0xFFFFFFFFFFFFFFFF );
                    break;
                }
                z.high = 0x7FFE;
                break;
            }
        }
        return z;
    }
    if ( expField <= 0 ) {
        isTiny = TRUE;
        zx = savedZ;
        expField = zx.exp + 0x3FFF;
        if ( expField < -70 ) {
            zx.sig.a1 = ( zx.sig.a0 != 0 ) || ( zx.sig.a1 != 0 );
            zx.sig.a0 = 0;
        }
        else {
            while ( expField <= 0 ) {
                zx.sig = shortShift128RightJamming( zx.sig, 1 );
                ++expField;
            }
        }
        switch ( slow_floatx80_rounding_precision ) {
         case 32:
            zx = roundFloatXTo24( isTiny, zx );
            break;
         case 64:
            zx = roundFloatXTo53( isTiny, zx );
            break;
         default:
            zx = roundFloatXTo64( isTiny, zx );
            break;
        }
        expField = ( LIT64( 0x0080000000000000 ) <= zx.sig.a0 ) ? 1 : 0;
    }
    zx.sig = shortShift128RightJamming( zx.sig, 56 );
    z.low = zx.sig.a1;
    z.high = expField;
    if ( zx.sign ) z.high |= 0x8000;
    return z;

}

#endif

#ifdef FLOAT128

static floatX float128ToFloatX( float128 a )
{
    int32 expField;
    floatX ax;

    ax.isNaN = FALSE;
    ax.isInf = FALSE;
    ax.isZero = FALSE;
    ax.sign = ( ( a.high & LIT64( 0x8000000000000000 ) ) != 0 );
    expField = ( a.high>>48 ) & 0x7FFF;
    ax.sig.a1 = a.low;
    ax.sig.a0 = a.high & LIT64( 0x0000FFFFFFFFFFFF );
    if ( expField == 0 ) {
        if ( ( ax.sig.a0 == 0 ) && ( ax.sig.a1 == 0 ) ) {
            ax.isZero = TRUE;
        }
        else {
            expField = 1 - 0x3FFF;
            do {
                ax.sig = shortShift128Left( ax.sig, 1 );
                --expField;
            } while ( ax.sig.a0 < LIT64( 0x0001000000000000 ) );
            ax.exp = expField;
        }
    }
    else if ( expField == 0x7FFF ) {
        if ( ( ax.sig.a0 == 0 ) && ( ax.sig.a1 == 0 ) ) {
            ax.isInf = TRUE;
        }
        else {
            ax.isNaN = TRUE;
        }
    }
    else {
        ax.exp = expField - 0x3FFF;
        ax.sig.a0 |= LIT64( 0x0001000000000000 );
    }
    ax.sig = shortShift128Left( ax.sig, 7 );
    return ax;

}

static float128 floatXToFloat128( floatX zx )
{
    floatX savedZ;
    flag isTiny;
    int32 expField;
    float128 z;

    if ( zx.isZero ) {
        z.low = 0;
        z.high = zx.sign ? LIT64( 0x8000000000000000 ) : 0;
        return z;
    }
    if ( zx.isInf ) {
        z.low = 0;
        z.high =
              zx.sign ? LIT64( 0xFFFF000000000000 )
            : LIT64( 0x7FFF000000000000 );
        return z;
    }
    if ( zx.isNaN ) {
        z.high = z.low = LIT64( 0xFFFFFFFFFFFFFFFF );
        return z;
    }
    while ( LIT64( 0x0100000000000000 ) <= zx.sig.a0 ) {
        zx.sig = shortShift128RightJamming( zx.sig, 1 );
        ++zx.exp;
    }
    while ( zx.sig.a0 < LIT64( 0x0080000000000000 ) ) {
        zx.sig = shortShift128Left( zx.sig, 1 );
        --zx.exp;
    }
    savedZ = zx;
    isTiny =
           ( slow_float_detect_tininess == float_tininess_before_rounding )
        && ( zx.exp + 0x3FFF <= 0 );
    zx = roundFloatXTo113( isTiny, zx );
    expField = zx.exp + 0x3FFF;
    if ( 0x7FFF <= expField ) {
        slow_float_exception_flags |=
            float_flag_overflow | float_flag_inexact;
        if ( zx.sign ) {
            switch ( slow_float_rounding_mode ) {
             case float_round_nearest_even:
             case float_round_down:
                z.low = 0;
                z.high = LIT64( 0xFFFF000000000000 );
                break;
             case float_round_to_zero:
             case float_round_up:
                z.low = LIT64( 0xFFFFFFFFFFFFFFFF );
                z.high = LIT64( 0xFFFEFFFFFFFFFFFF );
                break;
            }
        }
        else {
            switch ( slow_float_rounding_mode ) {
             case float_round_nearest_even:
             case float_round_up:
                z.low = 0;
                z.high = LIT64( 0x7FFF000000000000 );
                break;
             case float_round_to_zero:
             case float_round_down:
                z.low = LIT64( 0xFFFFFFFFFFFFFFFF );
                z.high = LIT64( 0x7FFEFFFFFFFFFFFF );
                break;
            }
        }
        return z;
    }
    if ( expField <= 0 ) {
        isTiny = TRUE;
        zx = savedZ;
        expField = zx.exp + 0x3FFF;
        if ( expField < -120 ) {
            zx.sig.a1 = ( zx.sig.a0 != 0 ) || ( zx.sig.a1 != 0 );
            zx.sig.a0 = 0;
        }
        else {
            while ( expField <= 0 ) {
                zx.sig = shortShift128RightJamming( zx.sig, 1 );
                ++expField;
            }
        }
        zx = roundFloatXTo113( isTiny, zx );
        expField = ( LIT64( 0x0080000000000000 ) <= zx.sig.a0 ) ? 1 : 0;
    }
    zx.sig = shortShift128RightJamming( zx.sig, 7 );
    z.low = zx.sig.a1;
    z.high = expField;
    z.high <<= 48;
    if ( zx.sign ) z.high |= LIT64( 0x8000000000000000 );
    z.high |= zx.sig.a0 & LIT64( 0x0000FFFFFFFFFFFF );
    return z;

}

#endif

static floatX floatXInvalid( void )
{

    slow_float_exception_flags |= float_flag_invalid;
    return floatXNaN;

}

static floatX floatXRoundToInt( floatX ax )
{
  int32 shiftCount;

    if ( ax.isNaN || ax.isInf ) return ax;
    shiftCount = 112 - ax.exp;
    if ( shiftCount <= 0 ) return ax;
    if ( 119 < shiftCount ) {
        ax.exp = 112;
        ax.sig.a1 = ! ax.isZero;
        ax.sig.a0 = 0;
    }
    else {
        while ( 0 < shiftCount ) {
            ax.sig = shortShift128RightJamming( ax.sig, 1 );
            ++ax.exp;
            --shiftCount;
        }
    }
    ax = roundFloatXTo113( FALSE, ax );
    if ( ( ax.sig.a0 == 0 ) && ( ax.sig.a1 == 0 ) ) ax.isZero = TRUE;
    return ax;

}

static floatX floatXAdd( floatX ax, floatX bx )
{
    int32 expDiff;
    floatX zx;

    if ( ax.isNaN ) return ax;
    if ( bx.isNaN ) return bx;
    if ( ax.isInf && bx.isInf ) {
        if ( ax.sign == bx.sign ) return ax;
        return floatXInvalid();
    }
    if ( ax.isInf ) return ax;
    if ( bx.isInf ) return bx;
    if ( ax.isZero && bx.isZero ) {
        if ( ax.sign == bx.sign ) return ax;
        goto completeCancellation;
    }
    if (    ( ax.sign != bx.sign )
         && ( ax.exp == bx.exp )
         && eq128( ax.sig, bx.sig )
       ) {
 completeCancellation:
        return
              ( slow_float_rounding_mode == float_round_down ) ?
                  floatXNegativeZero
            : floatXPositiveZero;
    }
    if ( ax.isZero ) return bx;
    if ( bx.isZero ) return ax;
    expDiff = ax.exp - bx.exp;
    if ( expDiff < 0 ) {
        zx = ax;
        zx.exp = bx.exp;
        if ( expDiff < -120 ) {
            zx.sig.a1 = 1;
            zx.sig.a0 = 0;
        }
        else {
            while ( expDiff < 0 ) {
                zx.sig = shortShift128RightJamming( zx.sig, 1 );
                ++expDiff;
            }
        }
        if ( ax.sign != bx.sign ) zx.sig = neg128( zx.sig );
        zx.sign = bx.sign;
        zx.sig = add128( zx.sig, bx.sig );
    }
    else {
        zx = bx;
        zx.exp = ax.exp;
        if ( 120 < expDiff ) {
            zx.sig.a1 = 1;
            zx.sig.a0 = 0;
        }
        else {
            while ( 0 < expDiff ) {
                zx.sig = shortShift128RightJamming( zx.sig, 1 );
                --expDiff;
            }
        }
        if ( ax.sign != bx.sign ) zx.sig = neg128( zx.sig );
        zx.sign = ax.sign;
        zx.sig = add128( zx.sig, ax.sig );
    }
    if ( zx.sig.a0 & LIT64( 0x8000000000000000 ) ) {
        zx.sig = neg128( zx.sig );
        zx.sign = ! zx.sign;
    }
    return zx;

}

static floatX floatXMul( floatX ax, floatX bx )
{
    int8 bitNum;
    floatX zx;

    if ( ax.isNaN ) return ax;
    if ( bx.isNaN ) return bx;
    if ( ax.isInf ) {
        if ( bx.isZero ) return floatXInvalid();
        if ( bx.sign ) ax.sign = ! ax.sign;
        return ax;
    }
    if ( bx.isInf ) {
        if ( ax.isZero ) return floatXInvalid();
        if ( ax.sign ) bx.sign = ! bx.sign;
        return bx;
    }
    zx = ax;
    zx.sign ^= bx.sign;
    if ( ax.isZero || bx.isZero ) {
        return zx.sign ? floatXNegativeZero : floatXPositiveZero;
    }
    zx.exp += bx.exp + 1;
    zx.sig.a1 = 0;
    zx.sig.a0 = 0;
    for ( bitNum = 0; bitNum < 119; ++bitNum ) {
        if ( bx.sig.a1 & 2 ) zx.sig = add128( zx.sig, ax.sig );
        bx.sig = shortShift128RightJamming( bx.sig, 1 );
        zx.sig = shortShift128RightJamming( zx.sig, 1 );
    }
    return zx;

}

static floatX floatXDiv( floatX ax, floatX bx )
{
    bits128X negBSig;
    int8 bitNum;
    floatX zx;

    if ( ax.isNaN ) return ax;
    if ( bx.isNaN ) return bx;
    if ( ax.isInf ) {
        if ( bx.isInf ) return floatXInvalid();
        if ( bx.sign ) ax.sign = ! ax.sign;
        return ax;
    }
    if ( bx.isZero ) {
        if ( ax.isZero ) return floatXInvalid();
        slow_float_exception_flags |= float_flag_divbyzero;
        if ( ax.sign ) bx.sign = ! bx.sign;
        bx.isZero = FALSE;
        bx.isInf = TRUE;
        return bx;
    }
    zx = ax;
    zx.sign ^= bx.sign;
    if ( ax.isZero || bx.isInf ) {
        return zx.sign ? floatXNegativeZero : floatXPositiveZero;
    }
    zx.exp -= bx.exp + 1;
    zx.sig.a1 = 0;
    zx.sig.a0 = 0;
    negBSig = neg128( bx.sig );
    for ( bitNum = 0; bitNum < 120; ++bitNum ) {
        if ( le128( bx.sig, ax.sig ) ) {
            zx.sig.a1 |= 1;
            ax.sig = add128( ax.sig, negBSig );
        }
        ax.sig = shortShift128Left( ax.sig, 1 );
        zx.sig = shortShift128Left( zx.sig, 1 );
    }
    if ( ax.sig.a0 || ax.sig.a1 ) zx.sig.a1 |= 1;
    return zx;

}

static floatX floatXRem( floatX ax, floatX bx )
{
    bits128X negBSig;
    flag lastQuotientBit;
    bits128X savedASig;

    if ( ax.isNaN ) return ax;
    if ( bx.isNaN ) return bx;
    if ( ax.isInf || bx.isZero ) return floatXInvalid();
    if ( ax.isZero || bx.isInf ) return ax;
    --bx.exp;
    if ( ax.exp < bx.exp ) return ax;
    bx.sig = shortShift128Left( bx.sig, 1 );
    negBSig = neg128( bx.sig );
    while ( bx.exp < ax.exp ) {
        if ( le128( bx.sig, ax.sig ) ) ax.sig = add128( ax.sig, negBSig );
        ax.sig = shortShift128Left( ax.sig, 1 );
        --ax.exp;
    }
    lastQuotientBit = le128( bx.sig, ax.sig );
    if ( lastQuotientBit ) ax.sig = add128( ax.sig, negBSig );
    savedASig = ax.sig;
    ax.sig = neg128( add128( ax.sig, negBSig ) );
    if ( lt128( ax.sig, savedASig ) ) {
        ax.sign = ! ax.sign;
    }
    else if ( lt128( savedASig, ax.sig ) ) {
        ax.sig = savedASig;
    }
    else {
        if ( lastQuotientBit ) {
            ax.sign = ! ax.sign;
        }
        else {
            ax.sig = savedASig;
        }
    }
    if ( ( ax.sig.a0 == 0 ) && ( ax.sig.a1 == 0 ) ) ax.isZero = TRUE;
    return ax;

}

static floatX floatXSqrt( floatX ax )
{
    int8 bitNum;
    bits128X bitSig, savedASig;
    floatX zx;

    if ( ax.isNaN || ax.isZero ) return ax;
    if ( ax.sign ) return floatXInvalid();
    if ( ax.isInf ) return ax;
    zx = ax;
    zx.exp >>= 1;
    if ( ( ax.exp & 1 ) == 0 ) ax.sig = shortShift128RightJamming( ax.sig, 1 );
    zx.sig.a1 = 0;
    zx.sig.a0 = 0;
    bitSig.a1 = 0;
    bitSig.a0 = LIT64( 0x0080000000000000 );
    for ( bitNum = 0; bitNum < 120; ++bitNum ) {
        savedASig = ax.sig;
        ax.sig = add128( ax.sig, neg128( zx.sig ) );
        ax.sig = shortShift128Left( ax.sig, 1 );
        ax.sig = add128( ax.sig, neg128( bitSig ) );
        if ( ax.sig.a0 & LIT64( 0x8000000000000000 ) ) {
            ax.sig = shortShift128Left( savedASig, 1 );
        }
        else {
            zx.sig.a1 |= bitSig.a1;
            zx.sig.a0 |= bitSig.a0;
        }
        bitSig = shortShift128RightJamming( bitSig, 1 );
    }
    if ( ax.sig.a0 || ax.sig.a1 ) zx.sig.a1 |= 1;
    return zx;

}

static flag floatXEq( floatX ax, floatX bx )
{

    if ( ax.isNaN || bx.isNaN ) return FALSE;
    if ( ax.isZero && bx.isZero ) return TRUE;
    if ( ax.sign != bx.sign ) return FALSE;
    if ( ax.isInf || bx.isInf ) return ax.isInf && bx.isInf;
    return ( ax.exp == bx.exp ) && eq128( ax.sig, bx.sig );

}

static flag floatXLe( floatX ax, floatX bx )
{

    if ( ax.isNaN || bx.isNaN ) return FALSE;
    if ( ax.isZero && bx.isZero ) return TRUE;
    if ( ax.sign != bx.sign ) return ax.sign;
    if ( ax.sign ) {
        if ( ax.isInf || bx.isZero ) return TRUE;
        if ( bx.isInf || ax.isZero ) return FALSE;
        if ( bx.exp < ax.exp ) return TRUE;
        if ( ax.exp < bx.exp ) return FALSE;
        return le128( bx.sig, ax.sig );
    }
    else {
        if ( bx.isInf || ax.isZero ) return TRUE;
        if ( ax.isInf || bx.isZero ) return FALSE;
        if ( ax.exp < bx.exp ) return TRUE;
        if ( bx.exp < ax.exp ) return FALSE;
        return le128( ax.sig, bx.sig );
    }

}

static flag floatXLt( floatX ax, floatX bx )
{

    if ( ax.isNaN || bx.isNaN ) return FALSE;
    if ( ax.isZero && bx.isZero ) return FALSE;
    if ( ax.sign != bx.sign ) return ax.sign;
    if ( ax.isInf && bx.isInf ) return FALSE;
    if ( ax.sign ) {
        if ( ax.isInf || bx.isZero ) return TRUE;
        if ( bx.isInf || ax.isZero ) return FALSE;
        if ( bx.exp < ax.exp ) return TRUE;
        if ( ax.exp < bx.exp ) return FALSE;
        return lt128( bx.sig, ax.sig );
    }
    else {
        if ( bx.isInf || ax.isZero ) return TRUE;
        if ( ax.isInf || bx.isZero ) return FALSE;
        if ( ax.exp < bx.exp ) return TRUE;
        if ( bx.exp < ax.exp ) return FALSE;
        return lt128( ax.sig, bx.sig );
    }

}

float32 slow_int32_to_float32( int32 a )
{

    return floatXToFloat32( int32ToFloatX( a ) );

}

float64 slow_int32_to_float64( int32 a )
{

    return floatXToFloat64( int32ToFloatX( a ) );

}

#ifdef FLOATX80

floatx80 slow_int32_to_floatx80( int32 a )
{

    return floatXToFloatx80( int32ToFloatX( a ) );

}

#endif

#ifdef FLOAT128

float128 slow_int32_to_float128( int32 a )
{

    return floatXToFloat128( int32ToFloatX( a ) );

}

#endif

float32 slow_int64_to_float32( int64 a )
{

    return floatXToFloat32( int64ToFloatX( a ) );

}

float64 slow_int64_to_float64( int64 a )
{

    return floatXToFloat64( int64ToFloatX( a ) );

}

#ifdef FLOATX80

floatx80 slow_int64_to_floatx80( int64 a )
{

    return floatXToFloatx80( int64ToFloatX( a ) );

}

#endif

#ifdef FLOAT128

float128 slow_int64_to_float128( int64 a )
{

    return floatXToFloat128( int64ToFloatX( a ) );

}

#endif

int32 slow_float32_to_int32( float32 a )
{

    return floatXToInt32( float32ToFloatX( a ) );

}

int32 slow_float32_to_int32_round_to_zero( float32 a )
{
    int8 savedRoundingMode;
    int32 z;

    savedRoundingMode = slow_float_rounding_mode;
    slow_float_rounding_mode = float_round_to_zero;
    z = floatXToInt32( float32ToFloatX( a ) );
    slow_float_rounding_mode = savedRoundingMode;
    return z;

}

int64 slow_float32_to_int64( float32 a )
{

    return floatXToInt64( float32ToFloatX( a ) );

}

int64 slow_float32_to_int64_round_to_zero( float32 a )
{
    int8 savedRoundingMode;
    int64 z;

    savedRoundingMode = slow_float_rounding_mode;
    slow_float_rounding_mode = float_round_to_zero;
    z = floatXToInt64( float32ToFloatX( a ) );
    slow_float_rounding_mode = savedRoundingMode;
    return z;

}

float64 slow_float32_to_float64( float32 a )
{

    return floatXToFloat64( float32ToFloatX( a ) );

}

#ifdef FLOATX80

floatx80 slow_float32_to_floatx80( float32 a )
{

    return floatXToFloatx80( float32ToFloatX( a ) );

}

#endif

#ifdef FLOAT128

float128 slow_float32_to_float128( float32 a )
{

    return floatXToFloat128( float32ToFloatX( a ) );

}

#endif

float32 slow_float32_round_to_int( float32 a )
{

    return floatXToFloat32( floatXRoundToInt( float32ToFloatX( a ) ) );

}

float32 slow_float32_add( float32 a, float32 b )
{

    return
        floatXToFloat32(
            floatXAdd( float32ToFloatX( a ), float32ToFloatX( b ) ) );

}

float32 slow_float32_sub( float32 a, float32 b )
{

    b ^= 0x80000000;
    return
        floatXToFloat32(
            floatXAdd( float32ToFloatX( a ), float32ToFloatX( b ) ) );

}

float32 slow_float32_mul( float32 a, float32 b )
{

    return
        floatXToFloat32(
            floatXMul( float32ToFloatX( a ), float32ToFloatX( b ) ) );

}

float32 slow_float32_div( float32 a, float32 b )
{

    return
        floatXToFloat32(
            floatXDiv( float32ToFloatX( a ), float32ToFloatX( b ) ) );

}

float32 slow_float32_rem( float32 a, float32 b )
{

    return
        floatXToFloat32(
            floatXRem( float32ToFloatX( a ), float32ToFloatX( b ) ) );

}

float32 slow_float32_sqrt( float32 a )
{

    return floatXToFloat32( floatXSqrt( float32ToFloatX( a ) ) );

}

flag slow_float32_eq( float32 a, float32 b )
{

    return floatXEq( float32ToFloatX( a ), float32ToFloatX( b ) );

}

flag slow_float32_le( float32 a, float32 b )
{
    floatX ax, bx;

    ax = float32ToFloatX( a );
    bx = float32ToFloatX( b );
    if ( ax.isNaN || bx.isNaN ) {
        slow_float_exception_flags |= float_flag_invalid;
    }
    return floatXLe( ax, bx );

}

flag slow_float32_lt( float32 a, float32 b )
{
    floatX ax, bx;

    ax = float32ToFloatX( a );
    bx = float32ToFloatX( b );
    if ( ax.isNaN || bx.isNaN ) {
        slow_float_exception_flags |= float_flag_invalid;
    }
    return floatXLt( ax, bx );

}

flag slow_float32_eq_signaling( float32 a, float32 b )
{
    floatX ax, bx;

    ax = float32ToFloatX( a );
    bx = float32ToFloatX( b );
    if ( ax.isNaN || bx.isNaN ) {
        slow_float_exception_flags |= float_flag_invalid;
    }
    return floatXEq( ax, bx );

}

flag slow_float32_le_quiet( float32 a, float32 b )
{

    return floatXLe( float32ToFloatX( a ), float32ToFloatX( b ) );

}

flag slow_float32_lt_quiet( float32 a, float32 b )
{

    return floatXLt( float32ToFloatX( a ), float32ToFloatX( b ) );

}

int32 slow_float64_to_int32( float64 a )
{

    return floatXToInt32( float64ToFloatX( a ) );

}

int32 slow_float64_to_int32_round_to_zero( float64 a )
{
    int8 savedRoundingMode;
    int32 z;

    savedRoundingMode = slow_float_rounding_mode;
    slow_float_rounding_mode = float_round_to_zero;
    z = floatXToInt32( float64ToFloatX( a ) );
    slow_float_rounding_mode = savedRoundingMode;
    return z;

}

int64 slow_float64_to_int64( float64 a )
{

    return floatXToInt64( float64ToFloatX( a ) );

}

int64 slow_float64_to_int64_round_to_zero( float64 a )
{
    int8 savedRoundingMode;
    int64 z;

    savedRoundingMode = slow_float_rounding_mode;
    slow_float_rounding_mode = float_round_to_zero;
    z = floatXToInt64( float64ToFloatX( a ) );
    slow_float_rounding_mode = savedRoundingMode;
    return z;

}

float32 slow_float64_to_float32( float64 a )
{

    return floatXToFloat32( float64ToFloatX( a ) );

}

#ifdef FLOATX80

floatx80 slow_float64_to_floatx80( float64 a )
{

    return floatXToFloatx80( float64ToFloatX( a ) );

}

#endif

#ifdef FLOAT128

float128 slow_float64_to_float128( float64 a )
{

    return floatXToFloat128( float64ToFloatX( a ) );

}

#endif

float64 slow_float64_round_to_int( float64 a )
{

    return floatXToFloat64( floatXRoundToInt( float64ToFloatX( a ) ) );

}

float64 slow_float64_add( float64 a, float64 b )
{

    return
        floatXToFloat64(
            floatXAdd( float64ToFloatX( a ), float64ToFloatX( b ) ) );

}

float64 slow_float64_sub( float64 a, float64 b )
{

    b ^= LIT64( 0x8000000000000000 );
    return
        floatXToFloat64(
            floatXAdd( float64ToFloatX( a ), float64ToFloatX( b ) ) );

}

float64 slow_float64_mul( float64 a, float64 b )
{

    return
        floatXToFloat64(
            floatXMul( float64ToFloatX( a ), float64ToFloatX( b ) ) );

}

float64 slow_float64_div( float64 a, float64 b )
{

    return
        floatXToFloat64(
            floatXDiv( float64ToFloatX( a ), float64ToFloatX( b ) ) );

}

float64 slow_float64_rem( float64 a, float64 b )
{

    return
        floatXToFloat64(
            floatXRem( float64ToFloatX( a ), float64ToFloatX( b ) ) );

}

float64 slow_float64_sqrt( float64 a )
{

    return floatXToFloat64( floatXSqrt( float64ToFloatX( a ) ) );

}

flag slow_float64_eq( float64 a, float64 b )
{

    return floatXEq( float64ToFloatX( a ), float64ToFloatX( b ) );

}

flag slow_float64_le( float64 a, float64 b )
{
    floatX ax, bx;

    ax = float64ToFloatX( a );
    bx = float64ToFloatX( b );
    if ( ax.isNaN || bx.isNaN ) {
        slow_float_exception_flags |= float_flag_invalid;
    }
    return floatXLe( ax, bx );

}

flag slow_float64_lt( float64 a, float64 b )
{
    floatX ax, bx;

    ax = float64ToFloatX( a );
    bx = float64ToFloatX( b );
    if ( ax.isNaN || bx.isNaN ) {
        slow_float_exception_flags |= float_flag_invalid;
    }
    return floatXLt( ax, bx );

}

flag slow_float64_eq_signaling( float64 a, float64 b )
{
    floatX ax, bx;

    ax = float64ToFloatX( a );
    bx = float64ToFloatX( b );
    if ( ax.isNaN || bx.isNaN ) {
        slow_float_exception_flags |= float_flag_invalid;
    }
    return floatXEq( ax, bx );

}

flag slow_float64_le_quiet( float64 a, float64 b )
{

    return floatXLe( float64ToFloatX( a ), float64ToFloatX( b ) );

}

flag slow_float64_lt_quiet( float64 a, float64 b )
{

    return floatXLt( float64ToFloatX( a ), float64ToFloatX( b ) );

}

#ifdef FLOATX80

int32 slow_floatx80_to_int32( floatx80 a )
{

    return floatXToInt32( floatx80ToFloatX( a ) );

}

int32 slow_floatx80_to_int32_round_to_zero( floatx80 a )
{
    int8 savedRoundingMode;
    int32 z;

    savedRoundingMode = slow_float_rounding_mode;
    slow_float_rounding_mode = float_round_to_zero;
    z = floatXToInt32( floatx80ToFloatX( a ) );
    slow_float_rounding_mode = savedRoundingMode;
    return z;

}

int64 slow_floatx80_to_int64( floatx80 a )
{

    return floatXToInt64( floatx80ToFloatX( a ) );

}

int64 slow_floatx80_to_int64_round_to_zero( floatx80 a )
{
    int8 savedRoundingMode;
    int64 z;

    savedRoundingMode = slow_float_rounding_mode;
    slow_float_rounding_mode = float_round_to_zero;
    z = floatXToInt64( floatx80ToFloatX( a ) );
    slow_float_rounding_mode = savedRoundingMode;
    return z;

}

float32 slow_floatx80_to_float32( floatx80 a )
{

    return floatXToFloat32( floatx80ToFloatX( a ) );

}

float64 slow_floatx80_to_float64( floatx80 a )
{

    return floatXToFloat64( floatx80ToFloatX( a ) );

}

#ifdef FLOAT128

float128 slow_floatx80_to_float128( floatx80 a )
{

    return floatXToFloat128( floatx80ToFloatX( a ) );

}

#endif

floatx80 slow_floatx80_round_to_int( floatx80 a )
{

    return floatXToFloatx80( floatXRoundToInt( floatx80ToFloatX( a ) ) );

}

floatx80 slow_floatx80_add( floatx80 a, floatx80 b )
{

    return
        floatXToFloatx80(
            floatXAdd( floatx80ToFloatX( a ), floatx80ToFloatX( b ) ) );

}

floatx80 slow_floatx80_sub( floatx80 a, floatx80 b )
{

    b.high ^= 0x8000;
    return
        floatXToFloatx80(
            floatXAdd( floatx80ToFloatX( a ), floatx80ToFloatX( b ) ) );

}

floatx80 slow_floatx80_mul( floatx80 a, floatx80 b )
{

    return
        floatXToFloatx80(
            floatXMul( floatx80ToFloatX( a ), floatx80ToFloatX( b ) ) );

}

floatx80 slow_floatx80_div( floatx80 a, floatx80 b )
{

    return
        floatXToFloatx80(
            floatXDiv( floatx80ToFloatX( a ), floatx80ToFloatX( b ) ) );

}

floatx80 slow_floatx80_rem( floatx80 a, floatx80 b )
{

    return
        floatXToFloatx80(
            floatXRem( floatx80ToFloatX( a ), floatx80ToFloatX( b ) ) );

}

floatx80 slow_floatx80_sqrt( floatx80 a )
{

    return floatXToFloatx80( floatXSqrt( floatx80ToFloatX( a ) ) );

}

flag slow_floatx80_eq( floatx80 a, floatx80 b )
{

    return floatXEq( floatx80ToFloatX( a ), floatx80ToFloatX( b ) );

}

flag slow_floatx80_le( floatx80 a, floatx80 b )
{
    floatX ax, bx;

    ax = floatx80ToFloatX( a );
    bx = floatx80ToFloatX( b );
    if ( ax.isNaN || bx.isNaN ) {
        slow_float_exception_flags |= float_flag_invalid;
    }
    return floatXLe( ax, bx );

}

flag slow_floatx80_lt( floatx80 a, floatx80 b )
{
    floatX ax, bx;

    ax = floatx80ToFloatX( a );
    bx = floatx80ToFloatX( b );
    if ( ax.isNaN || bx.isNaN ) {
        slow_float_exception_flags |= float_flag_invalid;
    }
    return floatXLt( ax, bx );

}

flag slow_floatx80_eq_signaling( floatx80 a, floatx80 b )
{
    floatX ax, bx;

    ax = floatx80ToFloatX( a );
    bx = floatx80ToFloatX( b );
    if ( ax.isNaN || bx.isNaN ) {
        slow_float_exception_flags |= float_flag_invalid;
    }
    return floatXEq( ax, bx );

}

flag slow_floatx80_le_quiet( floatx80 a, floatx80 b )
{

    return floatXLe( floatx80ToFloatX( a ), floatx80ToFloatX( b ) );

}

flag slow_floatx80_lt_quiet( floatx80 a, floatx80 b )
{

    return floatXLt( floatx80ToFloatX( a ), floatx80ToFloatX( b ) );

}

#endif

#ifdef FLOAT128

int32 slow_float128_to_int32( float128 a )
{

    return floatXToInt32( float128ToFloatX( a ) );

}

int32 slow_float128_to_int32_round_to_zero( float128 a )
{
    int8 savedRoundingMode;
    int32 z;

    savedRoundingMode = slow_float_rounding_mode;
    slow_float_rounding_mode = float_round_to_zero;
    z = floatXToInt32( float128ToFloatX( a ) );
    slow_float_rounding_mode = savedRoundingMode;
    return z;

}

int64 slow_float128_to_int64( float128 a )
{

    return floatXToInt64( float128ToFloatX( a ) );

}

int64 slow_float128_to_int64_round_to_zero( float128 a )
{
    int8 savedRoundingMode;
    int64 z;

    savedRoundingMode = slow_float_rounding_mode;
    slow_float_rounding_mode = float_round_to_zero;
    z = floatXToInt64( float128ToFloatX( a ) );
    slow_float_rounding_mode = savedRoundingMode;
    return z;

}

float32 slow_float128_to_float32( float128 a )
{

    return floatXToFloat32( float128ToFloatX( a ) );

}

float64 slow_float128_to_float64( float128 a )
{

    return floatXToFloat64( float128ToFloatX( a ) );

}

#ifdef FLOATX80

floatx80 slow_float128_to_floatx80( float128 a )
{

    return floatXToFloatx80( float128ToFloatX( a ) );

}

#endif

float128 slow_float128_round_to_int( float128 a )
{

    return floatXToFloat128( floatXRoundToInt( float128ToFloatX( a ) ) );

}

float128 slow_float128_add( float128 a, float128 b )
{

    return
        floatXToFloat128(
            floatXAdd( float128ToFloatX( a ), float128ToFloatX( b ) ) );

}

float128 slow_float128_sub( float128 a, float128 b )
{

    b.high ^= LIT64( 0x8000000000000000 );
    return
        floatXToFloat128(
            floatXAdd( float128ToFloatX( a ), float128ToFloatX( b ) ) );

}

float128 slow_float128_mul( float128 a, float128 b )
{

    return
        floatXToFloat128(
            floatXMul( float128ToFloatX( a ), float128ToFloatX( b ) ) );

}

float128 slow_float128_div( float128 a, float128 b )
{

    return
        floatXToFloat128(
            floatXDiv( float128ToFloatX( a ), float128ToFloatX( b ) ) );

}

float128 slow_float128_rem( float128 a, float128 b )
{

    return
        floatXToFloat128(
            floatXRem( float128ToFloatX( a ), float128ToFloatX( b ) ) );

}

float128 slow_float128_sqrt( float128 a )
{

    return floatXToFloat128( floatXSqrt( float128ToFloatX( a ) ) );

}

flag slow_float128_eq( float128 a, float128 b )
{

    return floatXEq( float128ToFloatX( a ), float128ToFloatX( b ) );

}

flag slow_float128_le( float128 a, float128 b )
{
    floatX ax, bx;

    ax = float128ToFloatX( a );
    bx = float128ToFloatX( b );
    if ( ax.isNaN || bx.isNaN ) {
        slow_float_exception_flags |= float_flag_invalid;
    }
    return floatXLe( ax, bx );

}

flag slow_float128_lt( float128 a, float128 b )
{
    floatX ax, bx;

    ax = float128ToFloatX( a );
    bx = float128ToFloatX( b );
    if ( ax.isNaN || bx.isNaN ) {
        slow_float_exception_flags |= float_flag_invalid;
    }
    return floatXLt( ax, bx );

}

flag slow_float128_eq_signaling( float128 a, float128 b )
{
    floatX ax, bx;

    ax = float128ToFloatX( a );
    bx = float128ToFloatX( b );
    if ( ax.isNaN || bx.isNaN ) {
        slow_float_exception_flags |= float_flag_invalid;
    }
    return floatXEq( ax, bx );

}

flag slow_float128_le_quiet( float128 a, float128 b )
{

    return floatXLe( float128ToFloatX( a ), float128ToFloatX( b ) );

}

flag slow_float128_lt_quiet( float128 a, float128 b )
{

    return floatXLt( float128ToFloatX( a ), float128ToFloatX( b ) );

}

#endif

