Source to src/cpu/fpp-softfloat.h


Enter a symbol's name here to quickly find it.

/*
  * UAE - The Un*x Amiga Emulator
  *
  * MC68881 emulation
  *
  * Conversion routines for hosts knowing floating point format.
  *
  * Copyright 1996 Herman ten Brugge
  * Modified 2005 Peter Keunecke
  */

#ifndef FPP_H
#define FPP_H

#include <softfloat.h>

float_ctrl fp_ctrl;

#define	FPCR_ROUNDING_MODE	0x00000030
#define	FPCR_ROUND_NEAR		0x00000000
#define	FPCR_ROUND_ZERO		0x00000010
#define	FPCR_ROUND_MINF		0x00000020
#define	FPCR_ROUND_PINF		0x00000030

#define	FPCR_ROUNDING_PRECISION	0x000000c0
#define	FPCR_PRECISION_SINGLE	0x00000040
#define	FPCR_PRECISION_DOUBLE	0x00000080
#define FPCR_PRECISION_EXTENDED	0x00000000

extern uae_u32 fpp_get_fpsr (void);


/* Functions for setting host/library modes and getting status */
STATIC_INLINE void init_fp_mode(void)
{
    float_init(&fp_ctrl);
}
STATIC_INLINE void set_fp_mode(uae_u32 mode_control)
{
    set_float_detect_tininess(float_tininess_before_rounding, &fp_ctrl);
    
    switch(mode_control & FPCR_ROUNDING_PRECISION) {
        case FPCR_PRECISION_SINGLE: // single
            set_float_rounding_precision(32, &fp_ctrl);
            break;
        case FPCR_PRECISION_DOUBLE: // double
            set_float_rounding_precision(64, &fp_ctrl);
            break;
        case FPCR_PRECISION_EXTENDED: // extended
            set_float_rounding_precision(80, &fp_ctrl);
            break;
        default: // double
            set_float_rounding_precision(64, &fp_ctrl);
            break;
    }

    switch(mode_control & FPCR_ROUNDING_MODE) {
        case FPCR_ROUND_NEAR: // to neareset
            set_float_rounding_mode(float_round_nearest_even, &fp_ctrl);
            break;
        case FPCR_ROUND_ZERO: // to zero
            set_float_rounding_mode(float_round_to_zero, &fp_ctrl);
            break;
        case FPCR_ROUND_MINF: // to minus
            set_float_rounding_mode(float_round_down, &fp_ctrl);
            break;
        case FPCR_ROUND_PINF: // to plus
            set_float_rounding_mode(float_round_up, &fp_ctrl);
            break;
    }
}
STATIC_INLINE void get_fp_status(uae_u32 *status)
{
    int8 flags = get_float_exception_flags(&fp_ctrl);
    
    if (flags & float_flag_signaling)
        *status |= 0x4000;
    if (flags & float_flag_invalid)
        *status |= 0x2000;
    if (flags & float_flag_overflow)
        *status |= 0x1000;
    if (flags & float_flag_underflow)
        *status |= 0x0800;
    if (flags & float_flag_divbyzero)
        *status |= 0x0400;
    if (flags & float_flag_inexact)
        *status |= 0x0200;
    if (flags & float_flag_decimal)
        *status |= 0x0100;
}
STATIC_INLINE void clear_fp_status(void)
{
    set_float_exception_flags(0, &fp_ctrl);
}

/* Helper functions */
STATIC_INLINE const char *fp_print(fptype *fx)
{
    static char fs[32];
    bool n, u, d;
    fptype x;
    int32 len;
    int8 save_exception_flags;
    
    n = floatx80_is_negative(*fx);
    u = floatx80_is_unnormal(*fx);
    d = floatx80_is_denormal(*fx);

    if (floatx80_is_infinity(*fx)) {
        sprintf(fs, "%c%s", n?'-':'+', "inf");
    } else if (floatx80_is_signaling_nan(*fx)) {
        sprintf(fs, "%c%s", n?'-':'+', "snan");
    } else if (floatx80_is_nan(*fx)) {
        sprintf(fs, "%c%s", n?'-':'+', "nan");
    } else {
        len = 17;
        save_exception_flags = get_float_exception_flags(&fp_ctrl);
        set_float_exception_flags(0, &fp_ctrl);
        x = floatx80_to_floatdecimal(*fx, &len, &fp_ctrl);
        
        sprintf(fs, "%c%01lld.%016llde%c%04d%s%s", n?'-':'+',
                x.low/LIT64(10000000000000000), x.low%LIT64(10000000000000000),
                (x.high&0x4000)?'-':'+', x.high&0x3FFF, d?"D":u?"U":"",
                (get_float_exception_flags(&fp_ctrl)&float_flag_inexact)?"~":"");

        set_float_exception_flags(save_exception_flags, &fp_ctrl);
    }
    
    return fs;
}

/* Functions for detecting float type */
STATIC_INLINE bool fp_is_snan(fptype *fp)
{
    return floatx80_is_signaling_nan(*fp) != 0;
}
STATIC_INLINE void fp_unset_snan(fptype *fp)
{
    fp->low |= LIT64(0x4000000000000000);
}
STATIC_INLINE bool fp_is_nan (fptype *fp)
{
    return floatx80_is_nan(*fp) != 0;
}
STATIC_INLINE bool fp_is_infinity (fptype *fp)
{
    return floatx80_is_infinity(*fp) != 0;
}
STATIC_INLINE bool fp_is_zero(fptype *fp)
{
    return floatx80_is_zero(*fp) != 0;
}
STATIC_INLINE bool fp_is_neg(fptype *fp)
{
    return floatx80_is_negative(*fp) != 0;
}
STATIC_INLINE bool fp_is_denormal(fptype *fp)
{
    return floatx80_is_denormal(*fp) != 0;
}
STATIC_INLINE bool fp_is_unnormal(fptype *fp)
{
    return floatx80_is_unnormal(*fp) != 0;
}

/* Function for normalizing unnormals */
STATIC_INLINE void fp_normalize(fptype *fp)
{
    *fp = floatx80_normalize(*fp);
}

/* Functions for converting between float formats */
STATIC_INLINE void to_single(fptype *fp, uae_u32 wrd1)
{
    float32 f = wrd1;
    *fp = float32_to_floatx80_allowunnormal(f);
}
STATIC_INLINE uae_u32 from_single(fptype *fp)
{
    float32 f = floatx80_to_float32(*fp, &fp_ctrl);
    return f;
}

STATIC_INLINE void to_double(fptype *fp, uae_u32 wrd1, uae_u32 wrd2)
{
    float64 f = ((float64)wrd1 << 32) | wrd2;
    *fp = float64_to_floatx80_allowunnormal(f);
}
STATIC_INLINE void from_double(fptype *fp, uae_u32 *wrd1, uae_u32 *wrd2)
{
    float64 f = floatx80_to_float64(*fp, &fp_ctrl);
    *wrd1 = f >> 32;
    *wrd2 = (uae_u32)f;
}

STATIC_INLINE void to_exten(fptype *fp, uae_u32 wrd1, uae_u32 wrd2, uae_u32 wrd3)
{
    fp->high = (uae_u16)(wrd1 >> 16);
    fp->low = ((uae_u64)wrd2 << 32) | wrd3;
}
STATIC_INLINE void from_exten(fptype *fp, uae_u32 *wrd1, uae_u32 *wrd2, uae_u32 *wrd3)
{
    floatx80 f = floatx80_to_floatx80(*fp, &fp_ctrl);
    *wrd1 = (uae_u32)(f.high << 16);
    *wrd2 = f.low >> 32;
    *wrd3 = (uae_u32)f.low;
}
STATIC_INLINE void to_exten_fmovem(fptype *fp, uae_u32 wrd1, uae_u32 wrd2, uae_u32 wrd3)
{
    fp->high = (uae_u16)(wrd1 >> 16);
    fp->low = ((uae_u64)wrd2 << 32) | wrd3;
}
STATIC_INLINE void from_exten_fmovem(fptype *fp, uae_u32 *wrd1, uae_u32 *wrd2, uae_u32 *wrd3)
{
    *wrd1 = (uae_u32)(fp->high << 16);
    *wrd2 = fp->low >> 32;
    *wrd3 = (uae_u32)fp->low;
}

STATIC_INLINE uae_s64 to_int(fptype *src, int size)
{
    switch (size) {
        case 0: return floatx80_to_int8(*src, &fp_ctrl);
        case 1: return floatx80_to_int16(*src, &fp_ctrl);
        case 2: return floatx80_to_int32(*src, &fp_ctrl);
        default: return 0;
    }
}
STATIC_INLINE fptype from_int(uae_s32 src)
{
    return int32_to_floatx80(src);
}

STATIC_INLINE void to_pack(fptype *fp, uae_u32 *wrd)
{
    floatx80 f;
    int i;
    uae_s32 exp;
    uae_s64 mant;
    uae_u32 pack_exp, pack_int, pack_se, pack_sm;
    uae_u64 pack_frac;
    
    if (((wrd[0] >> 16) & 0x7fff) == 0x7fff) {
        // infinity has extended exponent and all 0 packed fraction
        // nans are copies bit by bit
        to_exten(fp, wrd[0], wrd[1], wrd[2]);
        return;
    }
    if (!(wrd[0] & 0xf) && !wrd[1] && !wrd[2]) {
        // exponent is not cared about, if mantissa is zero
        wrd[0] &= 0x80000000;
        to_exten(fp, wrd[0], wrd[1], wrd[2]);
        return;
    }
    
    pack_exp = (wrd[0] >> 16) & 0xFFF;              // packed exponent
    pack_int = wrd[0] & 0xF;                        // packed integer part
    pack_frac = ((uae_u64)wrd[1] << 32) | wrd[2];   // packed fraction
    pack_se = (wrd[0] >> 30) & 1;                   // sign of packed exponent
    pack_sm = (wrd[0] >> 31) & 1;                   // sign of packed significand
    
    exp = 0;
    
    for (i = 0; i < 3; i++) {
        exp *= 10;
        exp += (pack_exp >> (8 - i * 4)) & 0xF;
    }
    
    if (pack_se) {
        exp = -exp;
    }
    
    exp -= 16;
    
    if (exp < 0) {
        exp = -exp;
        pack_se = 1;
    }
    
    mant = pack_int;
    
    for (i = 0; i < 16; i++) {
        mant *= 10;
        mant += (pack_frac >> (60 - i * 4)) & 0xF;
    }

    f.high = exp & 0x3FFF;
    f.high |= pack_se ? 0x4000 : 0;
    f.high |= pack_sm ? 0x8000 : 0;
    f.low = mant;
    
    *fp = floatdecimal_to_floatx80(f, &fp_ctrl);
}
STATIC_INLINE void from_pack(fptype *fp, uae_u32 *wrd, uae_s32 kfactor)
{
    floatx80 f = floatx80_to_floatdecimal(*fp, &kfactor, &fp_ctrl);
    
    uae_u32 pack_exp, pack_exp4, pack_int, pack_se, pack_sm;
    uae_u64 pack_frac;
    
    uae_u32 exponent;
    uae_u64 significand;

    uae_s32 len;
    uae_u64 digit;
    
    if ((f.high & 0x7FFF) == 0x7FFF) {
        wrd[0] = (uae_u32)(f.high << 16);
        wrd[1] = f.low >> 32;
        wrd[2] = (uae_u32)f.low;
    } else {
        exponent = f.high & 0x3FFF;
        significand = f.low;
        
        pack_int = 0;
        pack_frac = 0;
        len = kfactor; // SoftFloat saved len to kfactor variable
        while (len > 0) {
            len--;
            digit = significand % 10;
            significand /= 10;
            if (len == 0) {
                pack_int = digit;
            } else {
                pack_frac |= digit << (64 - len * 4);
            }
        }
        
        pack_exp = 0;
        pack_exp4 = 0;
        len = 4;
        while (len > 0) {
            len--;
            digit = exponent % 10;
            exponent /= 10;
            if (len == 0) {
                pack_exp4 = digit;
            } else {
                pack_exp |= digit << (12 - len * 4);
            }
        }
        
        pack_se = f.high & 0x4000;
        pack_sm = f.high & 0x8000;
        
        wrd[0] = pack_exp << 16;
        wrd[0] |= pack_exp4 << 12;
        wrd[0] |= pack_int;
        wrd[0] |= pack_se ? 0x40000000 : 0;
        wrd[0] |= pack_sm ? 0x80000000 : 0;
        
        wrd[1] = pack_frac >> 32;
        wrd[2] = pack_frac & 0xffffffff;
    }
}

/* Functions for returning exception state data */
STATIC_INLINE fptype fp_get_internal_overflow(void)
{
    return getFloatInternalOverflow(&fp_ctrl);
}
STATIC_INLINE fptype fp_get_internal_underflow(void)
{
    return getFloatInternalUnderflow(&fp_ctrl);
}
STATIC_INLINE fptype fp_get_internal_round_all(void)
{
    return getFloatInternalRoundedAll(&fp_ctrl);
}
STATIC_INLINE fptype fp_get_internal_round(void)
{
    return getFloatInternalRoundedSome(&fp_ctrl);
}
STATIC_INLINE fptype fp_get_internal_round_exten(void)
{
    return getFloatInternalFloatx80(&fp_ctrl);
}
STATIC_INLINE fptype fp_get_internal(void)
{
    return getFloatInternalUnrounded(&fp_ctrl);
}
STATIC_INLINE uae_u32 fp_get_internal_grs(void)
{
    return (uae_u32)getFloatInternalGRS(&fp_ctrl);
}

/* Function for denormalizing */
STATIC_INLINE void fp_denormalize(fptype *fp, int esign)
{
    *fp = floatx80_denormalize(*fp, esign);
}

/* Functions for rounding */

// round to float with extended precision exponent
STATIC_INLINE void fp_round32(fptype *fp)
{
    *fp = floatx80_round32(*fp, &fp_ctrl);
}

// round to double with extended precision exponent
STATIC_INLINE void fp_round64(fptype *fp)
{
    *fp = floatx80_round64(*fp, &fp_ctrl);
}

// round to float
STATIC_INLINE void fp_round_single(fptype *fp)
{
    *fp = floatx80_round_to_float32(*fp, &fp_ctrl);
}

// round to double
STATIC_INLINE void fp_round_double(fptype *fp)
{
    *fp = floatx80_round_to_float64(*fp, &fp_ctrl);
}

// round to selected precision
STATIC_INLINE void fp_round(fptype *a)
{
    switch(get_float_rounding_precision(&fp_ctrl)) {
        case 32:
            *a = floatx80_round_to_float32(*a, &fp_ctrl);
            break;
        case 64:
            *a = floatx80_round_to_float64(*a, &fp_ctrl);
            break;
        default:
            break;
    }
}

/* Arithmetic functions */
STATIC_INLINE void fp_move(fptype *a, fptype *b)
{
    *a = floatx80_move(*b, &fp_ctrl);
}
STATIC_INLINE void fp_int(fptype *a, fptype *b)
{
    *a = floatx80_round_to_int(*b, &fp_ctrl);
}
STATIC_INLINE void fp_intrz(fptype *a, fptype *b)
{
    *a = floatx80_round_to_int_toward_zero(*b, &fp_ctrl);
}
STATIC_INLINE void fp_sqrt(fptype *a, fptype *b)
{
    *a = floatx80_sqrt(*b, &fp_ctrl);
}
STATIC_INLINE void fp_abs(fptype *a, fptype *b)
{
    *a = floatx80_abs(*b, &fp_ctrl);
}
STATIC_INLINE void fp_neg(fptype *a, fptype *b)
{
    *a = floatx80_neg(*b, &fp_ctrl);
}
STATIC_INLINE void fp_getexp(fptype *a, fptype *b)
{
    *a = floatx80_getexp(*b, &fp_ctrl);
}
STATIC_INLINE void fp_getman(fptype *a, fptype *b)
{
    *a = floatx80_getman(*b, &fp_ctrl);
}
STATIC_INLINE void fp_div(fptype *a, fptype *b)
{
    *a = floatx80_div(*a, *b, &fp_ctrl);
}
STATIC_INLINE void fp_mod(fptype *a, fptype *b, uae_u64 *q, uae_s8 *s)
{
    *a = floatx80_mod(*a, *b, q, s, &fp_ctrl);
}
STATIC_INLINE void fp_add(fptype *a, fptype *b)
{
    *a = floatx80_add(*a, *b, &fp_ctrl);
}
STATIC_INLINE void fp_mul(fptype *a, fptype *b)
{
    *a = floatx80_mul(*a, *b, &fp_ctrl);
}
STATIC_INLINE void fp_sgldiv(fptype *a, fptype *b)
{
    *a = floatx80_sgldiv(*a, *b, &fp_ctrl);
}
STATIC_INLINE void fp_rem(fptype *a, fptype *b, uae_u64 *q, uae_s8 *s)
{
    *a = floatx80_rem(*a, *b, q, s, &fp_ctrl);
}
STATIC_INLINE void fp_scale(fptype *a, fptype *b)
{
    *a = floatx80_scale(*a, *b, &fp_ctrl);
}
STATIC_INLINE void fp_sglmul(fptype *a, fptype *b)
{
    *a = floatx80_sglmul(*a, *b, &fp_ctrl);
}
STATIC_INLINE void fp_sub(fptype *a, fptype *b)
{
    *a = floatx80_sub(*a, *b, &fp_ctrl);
}
STATIC_INLINE void fp_cmp(fptype *a, fptype *b)
{
    *a = floatx80_cmp(*a, *b, &fp_ctrl);
}
STATIC_INLINE void fp_tst(fptype *a, fptype *b)
{
    *a = floatx80_tst(*b, &fp_ctrl);
}

STATIC_INLINE void fp_sinh(fptype *a, fptype *b)
{
    *a = floatx80_sinh(*b, &fp_ctrl);
}
STATIC_INLINE void fp_lognp1(fptype *a, fptype *b)
{
    *a = floatx80_lognp1(*b, &fp_ctrl);
}
STATIC_INLINE void fp_etoxm1(fptype *a, fptype *b)
{
    *a = floatx80_etoxm1(*b, &fp_ctrl);
}
STATIC_INLINE void fp_tanh(fptype *a, fptype *b)
{
    *a = floatx80_tanh(*b, &fp_ctrl);
}
STATIC_INLINE void fp_atan(fptype *a, fptype *b)
{
    *a = floatx80_atan(*b, &fp_ctrl);
}
STATIC_INLINE void fp_asin(fptype *a, fptype *b)
{
    *a = floatx80_asin(*b, &fp_ctrl);
}
STATIC_INLINE void fp_atanh(fptype *a, fptype *b)
{
    *a = floatx80_atanh(*b, &fp_ctrl);
}
STATIC_INLINE void fp_sin(fptype *a, fptype *b)
{
    *a = floatx80_sin(*b, &fp_ctrl);
}
STATIC_INLINE void fp_tan(fptype *a, fptype *b)
{
    *a = floatx80_tan(*b, &fp_ctrl);
}
STATIC_INLINE void fp_etox(fptype *a, fptype *b)
{
    *a = floatx80_etox(*b, &fp_ctrl);
}
STATIC_INLINE void fp_twotox(fptype *a, fptype *b)
{
    *a = floatx80_twotox(*b, &fp_ctrl);
}
STATIC_INLINE void fp_tentox(fptype *a, fptype *b)
{
    *a = floatx80_tentox(*b, &fp_ctrl);
}
STATIC_INLINE void fp_logn(fptype *a, fptype *b)
{
    *a = floatx80_logn(*b, &fp_ctrl);
}
STATIC_INLINE void fp_log10(fptype *a, fptype *b)
{
    *a = floatx80_log10(*b, &fp_ctrl);
}
STATIC_INLINE void fp_log2(fptype *a, fptype *b)
{
    *a = floatx80_log2(*b, &fp_ctrl);
}
STATIC_INLINE void fp_cosh(fptype *a, fptype *b)
{
    *a = floatx80_cosh(*b, &fp_ctrl);
}
STATIC_INLINE void fp_acos(fptype *a, fptype *b)
{
    *a = floatx80_acos(*b, &fp_ctrl);
}
STATIC_INLINE void fp_cos(fptype *a, fptype *b)
{
    *a = floatx80_cos(*b, &fp_ctrl);
}

/* Functions with fixed precision */
STATIC_INLINE void fp_move_single(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(32, &fp_ctrl);
    *a = floatx80_move(*b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_abs_single(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(32, &fp_ctrl);
    *a = floatx80_abs(*b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_neg_single(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(32, &fp_ctrl);
    *a = floatx80_neg(*b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_add_single(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(32, &fp_ctrl);
    *a = floatx80_add(*a, *b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_sub_single(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(32, &fp_ctrl);
    *a = floatx80_sub(*a, *b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_mul_single(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(32, &fp_ctrl);
    *a = floatx80_mul(*a, *b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_div_single(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(32, &fp_ctrl);
    *a = floatx80_div(*a, *b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_sqrt_single(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(32, &fp_ctrl);
    *a = floatx80_sqrt(*b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_move_double(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(64, &fp_ctrl);
    *a = floatx80_move(*b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_abs_double(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(64, &fp_ctrl);
    *a = floatx80_abs(*b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_neg_double(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(64, &fp_ctrl);
    *a = floatx80_neg(*b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_add_double(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(64, &fp_ctrl);
    *a = floatx80_add(*a, *b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_sub_double(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(64, &fp_ctrl);
    *a = floatx80_sub(*a, *b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_mul_double(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(64, &fp_ctrl);
    *a = floatx80_mul(*a, *b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_div_double(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(64, &fp_ctrl);
    *a = floatx80_div(*a, *b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}
STATIC_INLINE void fp_sqrt_double(fptype *a, fptype *b)
{
    int8 oldprec = get_float_rounding_precision(&fp_ctrl);
    set_float_rounding_precision(64, &fp_ctrl);
    *a = floatx80_sqrt(*b, &fp_ctrl);
    set_float_rounding_precision(oldprec, &fp_ctrl);
}



#if 0 /* Old fallback functions disabled */
static const long double twoto32 = 4294967296.0;

STATIC_INLINE void to_native(long double *fp, fptype fpx)
{
    int expon;
    long double frac;
    
    expon = fpx.high & 0x7fff;
    
    if (floatx80_is_zero(fpx)) {
        *fp = floatx80_is_negative(fpx) ? -0.0 : +0.0;
        return;
    }
    if (floatx80_is_nan(fpx)) {
        *fp = sqrtl(-1);
        return;
    }
    if (floatx80_is_infinity(fpx)) {
        *fp = floatx80_is_negative(fpx) ? logl(0.0) : (1.0/0.0);
        return;
    }
    
    frac = (long double)fpx.low / (long double)(twoto32 * 2147483648.0);
    if (floatx80_is_negative(fpx))
        frac = -frac;
    *fp = ldexpl (frac, expon - 16383);
}
STATIC_INLINE void from_native(long double fp, fptype *fpx)
{
    int expon;
    long double frac;
    
    if (signbit(fp))
        fpx->high = 0x8000;
    else
        fpx->high = 0x0000;
    
    if (isnan(fp)) {
        fpx->high |= 0x7fff;
        fpx->low = LIT64(0xffffffffffffffff);
        return;
    }
    if (isinf(fp)) {
        fpx->high |= 0x7fff;
        fpx->low = LIT64(0x0000000000000000);
        return;
    }
    if (fp == 0.0) {
        fpx->low = LIT64(0x0000000000000000);
        return;
    }
    if (fp < 0.0)
        fp = -fp;
    
    frac = frexpl (fp, &expon);
    frac += 0.5 / (twoto32 * twoto32);
    if (frac >= 1.0) {
        frac /= 2.0;
        expon++;
    }
    fpx->high |= (expon + 16383 - 1) & 0x7fff;
    fpx->low = (bits64)(frac * (long double)(twoto32 * twoto32));
    
    while (!(fpx->low & LIT64( 0x8000000000000000))) {
        if (fpx->high == 0) {
            break;
        }
        fpx->low <<= 1;
        fpx->high--;
    }
}

STATIC_INLINE void fp_sinhf(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_sinh_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = sinhl(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_lognp1f(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_lognp1_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = log1pl(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_etoxm1f(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_etoxm1_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = expm1l(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_tanhf(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_tanh_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = tanhl(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_atanf(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_atan_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = atanl(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_asinf(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_asin_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = asinl(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_atanhf(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_atanh_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = atanhl(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_sinf(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_sin_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = sinl(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_tanf(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_tan_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = tanl(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_etoxf(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_etox_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = expl(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_twotoxf(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_twotox_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = powl(2.0, fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_tentoxf(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_tentox_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = powl(10.0, fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_lognf(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_logn_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = logl(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_log10f(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_log10_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = log10l(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_log2f(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_log2_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = log2l(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_coshf(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_cosh_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = coshl(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_acosf(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_acos_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = acosl(fp);
    from_native(fp, a);
    fp_round(a);
}
STATIC_INLINE void fp_cosf(fptype *a, fptype *b)
{
    flag e = 0;
    floatx80_cos_check(*a, &e);
    if (e) return;
    long double fp;
    to_native(&fp, *b);
    fp = cosl(fp);
    from_native(fp, a);
    fp_round(a);
}
#endif

#endif