root/arch/parisc/math-emu/fcnvfu.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Linux/PA-RISC Project (http://www.parisc-linux.org/)
 *
 * Floating-point emulation code
 *  Copyright (C) 2001 Hewlett-Packard (Paul Bame) <bame@debian.org>
 */
/*
 * BEGIN_DESC
 *
 *  File:
 *      @(#)    pa/spmath/fcnvfu.c              $Revision: 1.1 $
 *
 *  Purpose:
 *      Floating-point to Unsigned Fixed-point Converts
 *
 *  External Interfaces:
 *      dbl_to_dbl_fcnvfu(srcptr,_nullptr,dstptr,status)
 *      dbl_to_sgl_fcnvfu(srcptr,_nullptr,dstptr,status)
 *      sgl_to_dbl_fcnvfu(srcptr,_nullptr,dstptr,status)
 *      sgl_to_sgl_fcnvfu(srcptr,_nullptr,dstptr,status)
 *
 *  Internal Interfaces:
 *
 *  Theory:
 *      <<please update with a overview of the operation of this file>>
 *
 * END_DESC
*/


#include "float.h"
#include "sgl_float.h"
#include "dbl_float.h"
#include "cnv_float.h"

/************************************************************************
 *  Floating-point to Unsigned Fixed-point Converts                     *
 ************************************************************************/

/*
 *  Single Floating-point to Single Unsigned Fixed 
 */
/*ARGSUSED*/
int
sgl_to_sgl_fcnvfu(
                        sgl_floating_point *srcptr,
                        unsigned int *_nullptr,
                        unsigned int *dstptr,
                        unsigned int *status)
{
        register unsigned int src, result;
        register int src_exponent;
        register boolean inexact = FALSE;

        src = *srcptr;
        src_exponent = Sgl_exponent(src) - SGL_BIAS;

        /* 
         * Test for overflow
         */
        if (src_exponent > SGL_FX_MAX_EXP + 1) {
                if (Sgl_isone_sign(src)) {
                        result = 0;
                } else {
                        result = 0xffffffff;
                }
                if (Is_invalidtrap_enabled()) {
                        return(INVALIDEXCEPTION);
                }
                Set_invalidflag();
                *dstptr = result;
                return(NOEXCEPTION);
        }
        /*
         * Generate result
         */
        if (src_exponent >= 0) {
                /* 
                 * Check sign.
                 * If negative, trap unimplemented.
                 */
                if (Sgl_isone_sign(src)) {
                        result = 0;
                        if (Is_invalidtrap_enabled()) {
                                return(INVALIDEXCEPTION);
                        }
                        Set_invalidflag();
                        *dstptr = result;
                        return(NOEXCEPTION);
                }
                Sgl_clear_signexponent_set_hidden(src);
                Suint_from_sgl_mantissa(src,src_exponent,result);

                /* check for inexact */
                if (Sgl_isinexact_to_unsigned(src,src_exponent)) {
                        inexact = TRUE;
                        /*  round result  */
                        switch (Rounding_mode()) {
                        case ROUNDPLUS:
                                result++;
                                break;
                        case ROUNDMINUS: /* never negative */
                                break;
                        case ROUNDNEAREST:
                                if (Sgl_isone_roundbit(src,src_exponent) &&
                                    (Sgl_isone_stickybit(src,src_exponent) ||
                                     (result & 1))) {
                                        result++;
                                }
                                break;
                        }
                }
        } else {
                result = 0;

                /* check for inexact */
                if (Sgl_isnotzero_exponentmantissa(src)) {
                        inexact = TRUE;
                        /*  round result  */
                        switch (Rounding_mode()) {
                        case ROUNDPLUS:
                                if (Sgl_iszero_sign(src)) {
                                        result++;
                                }
                                break;
                        case ROUNDMINUS:
                                if (Sgl_isone_sign(src)) {
                                        result = 0;
                                        if (Is_invalidtrap_enabled()) {
                                                return(INVALIDEXCEPTION);
                                        }
                                        Set_invalidflag();
                                        inexact = FALSE;
                                }
                                break;
                        case ROUNDNEAREST:
                                if (src_exponent == -1 &&
                                    Sgl_isnotzero_mantissa(src)) {
                                        if (Sgl_isone_sign(src)) {
                                                result = 0;
                                                if (Is_invalidtrap_enabled()) {
                                                        return(INVALIDEXCEPTION);
                                                }
                                                Set_invalidflag();
                                                inexact = FALSE;
                                        }
                                        else result++;
                                }
                                break;
                        }
                }
        }
        *dstptr = result;
        if (inexact) {
                if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION);
                else Set_inexactflag();
        }
        return(NOEXCEPTION);
}

/*
 *  Single Floating-point to Double Unsigned Fixed 
 */
/*ARGSUSED*/
int
sgl_to_dbl_fcnvfu(
                    sgl_floating_point *srcptr,
                    unsigned int *_nullptr,
                    dbl_unsigned *dstptr,
                    unsigned int *status)
{
        register int src_exponent;
        register unsigned int src, resultp1, resultp2;
        register boolean inexact = FALSE;

        src = *srcptr;
        src_exponent = Sgl_exponent(src) - SGL_BIAS;

        /* 
         * Test for overflow
         */
        if (src_exponent > DBL_FX_MAX_EXP + 1) {
                if (Sgl_isone_sign(src)) {
                        resultp1 = resultp2 = 0;
                } else {
                        resultp1 = resultp2 = 0xffffffff;
                }
                if (Is_invalidtrap_enabled()) {
                        return(INVALIDEXCEPTION);
                }
                Set_invalidflag();
                Duint_copytoptr(resultp1,resultp2,dstptr);
                return(NOEXCEPTION);
        }
        /*
         * Generate result
         */
        if (src_exponent >= 0) {
                /* 
                 * Check sign.
                 * If negative, trap unimplemented.
                 */
                if (Sgl_isone_sign(src)) {
                        resultp1 = resultp2 = 0;
                        if (Is_invalidtrap_enabled()) {
                                return(INVALIDEXCEPTION);
                        }
                        Set_invalidflag();
                        Duint_copytoptr(resultp1,resultp2,dstptr);
                        return(NOEXCEPTION);
                }
                Sgl_clear_signexponent_set_hidden(src);
                Duint_from_sgl_mantissa(src,src_exponent,resultp1,resultp2);

                /* check for inexact */
                if (Sgl_isinexact_to_unsigned(src,src_exponent)) {
                        inexact = TRUE;
                        /*  round result  */
                        switch (Rounding_mode()) {
                        case ROUNDPLUS:
                                Duint_increment(resultp1,resultp2);
                                break;
                        case ROUNDMINUS: /* never negative */
                                break;
                        case ROUNDNEAREST:
                                if (Sgl_isone_roundbit(src,src_exponent) &&
                                    (Sgl_isone_stickybit(src,src_exponent) || 
                                     Duint_isone_lowp2(resultp2))) {
                                        Duint_increment(resultp1,resultp2);
                                }
                                break;
                        }
                }
        } else {
                Duint_setzero(resultp1,resultp2);

                /* check for inexact */
                if (Sgl_isnotzero_exponentmantissa(src)) {
                        inexact = TRUE;
                        /*  round result  */
                        switch (Rounding_mode()) {
                        case ROUNDPLUS:
                                if (Sgl_iszero_sign(src)) {
                                        Duint_increment(resultp1,resultp2);
                                }
                                break;
                        case ROUNDMINUS:
                                if (Sgl_isone_sign(src)) {
                                        resultp1 = resultp2 = 0;
                                        if (Is_invalidtrap_enabled()) {
                                                return(INVALIDEXCEPTION);
                                        }
                                        Set_invalidflag();
                                        inexact = FALSE;
                                }
                                break;
                        case ROUNDNEAREST:
                                if (src_exponent == -1 &&
                                    Sgl_isnotzero_mantissa(src)) {
                                        if (Sgl_isone_sign(src)) {
                                                resultp1 = 0;
                                                resultp2 = 0;
                                                if (Is_invalidtrap_enabled()) {
                                                        return(INVALIDEXCEPTION);
                                                }
                                                Set_invalidflag();
                                                inexact = FALSE;
                                        }
                                        else Duint_increment(resultp1,resultp2);
                                }
                        }
                }
        }
        Duint_copytoptr(resultp1,resultp2,dstptr);
        if (inexact) {
                if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION);
                else Set_inexactflag();
        }
        return(NOEXCEPTION);
}

/*
 *  Double Floating-point to Single Unsigned Fixed 
 */
/*ARGSUSED*/
int
dbl_to_sgl_fcnvfu (dbl_floating_point * srcptr, unsigned int *_nullptr,
                   unsigned int *dstptr, unsigned int *status)
{
        register unsigned int srcp1, srcp2, result;
        register int src_exponent;
        register boolean inexact = FALSE;

        Dbl_copyfromptr(srcptr,srcp1,srcp2);
        src_exponent = Dbl_exponent(srcp1) - DBL_BIAS;

        /* 
         * Test for overflow
         */
        if (src_exponent > SGL_FX_MAX_EXP + 1) {
                if (Dbl_isone_sign(srcp1)) {
                        result = 0;
                } else {
                        result = 0xffffffff;
                }
                if (Is_invalidtrap_enabled()) {
                        return(INVALIDEXCEPTION);
                }
                Set_invalidflag();
                *dstptr = result;
                return(NOEXCEPTION);
        }
        /*
         * Generate result
         */
        if (src_exponent >= 0) {
                /* 
                 * Check sign.
                 * If negative, trap unimplemented.
                 */
                if (Dbl_isone_sign(srcp1)) {
                        result = 0;
                        if (Is_invalidtrap_enabled()) {
                                return(INVALIDEXCEPTION);
                        }
                        Set_invalidflag();
                        *dstptr = result;
                        return(NOEXCEPTION);
                }
                Dbl_clear_signexponent_set_hidden(srcp1);
                Suint_from_dbl_mantissa(srcp1,srcp2,src_exponent,result);

                /* check for inexact */
                if (Dbl_isinexact_to_unsigned(srcp1,srcp2,src_exponent)) {
                        inexact = TRUE;
                        /*  round result  */
                        switch (Rounding_mode()) {
                        case ROUNDPLUS:
                             result++;
                             break;
                        case ROUNDMINUS: /* never negative */
                             break;
                        case ROUNDNEAREST:
                             if(Dbl_isone_roundbit(srcp1,srcp2,src_exponent) &&
                                (Dbl_isone_stickybit(srcp1,srcp2,src_exponent)||
                                 result&1))
                                   result++;
                             break;
                        }
                        /* check for overflow */
                        if (result == 0) {
                                result = 0xffffffff;
                                if (Is_invalidtrap_enabled()) {
                                        return(INVALIDEXCEPTION);
                                }
                                Set_invalidflag();
                                *dstptr = result;
                                return(NOEXCEPTION);
                        }
                }
        } else {
                result = 0;

                /* check for inexact */
                if (Dbl_isnotzero_exponentmantissa(srcp1,srcp2)) {
                        inexact = TRUE;
                        /*  round result  */
                        switch (Rounding_mode()) {
                        case ROUNDPLUS:
                                if (Dbl_iszero_sign(srcp1)) result++;
                                break;
                        case ROUNDMINUS:
                                if (Dbl_isone_sign(srcp1)) {
                                        result = 0;
                                        if (Is_invalidtrap_enabled()) {
                                                return(INVALIDEXCEPTION);
                                        }
                                        Set_invalidflag();
                                        inexact = FALSE;
                                }
                                break;
                        case ROUNDNEAREST:
                                if (src_exponent == -1 &&
                                    Dbl_isnotzero_mantissa(srcp1,srcp2))
                                        if (Dbl_isone_sign(srcp1)) {
                                                result = 0;
                                                if (Is_invalidtrap_enabled()) {
                                                        return(INVALIDEXCEPTION);
                                                }
                                                Set_invalidflag();
                                                inexact = FALSE;
                                        }
                                        else result++;
                        }
                }
        }
        *dstptr = result;
        if (inexact) {
                if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION);
                else Set_inexactflag();
        }
        return(NOEXCEPTION);
}

/*
 *  Double Floating-point to Double Unsigned Fixed 
 */
/*ARGSUSED*/
int
dbl_to_dbl_fcnvfu (dbl_floating_point * srcptr, unsigned int *_nullptr,
                   dbl_unsigned * dstptr, unsigned int *status)
{
        register int src_exponent;
        register unsigned int srcp1, srcp2, resultp1, resultp2;
        register boolean inexact = FALSE;

        Dbl_copyfromptr(srcptr,srcp1,srcp2);
        src_exponent = Dbl_exponent(srcp1) - DBL_BIAS;

        /* 
         * Test for overflow
         */
        if (src_exponent > DBL_FX_MAX_EXP + 1) {
                if (Dbl_isone_sign(srcp1)) {
                        resultp1 = resultp2 = 0;
                } else {
                        resultp1 = resultp2 = 0xffffffff;
                }
                if (Is_invalidtrap_enabled()) {
                        return(INVALIDEXCEPTION);
                }
                Set_invalidflag();
                Duint_copytoptr(resultp1,resultp2,dstptr);
                return(NOEXCEPTION);
        }
 
        /*
         * Generate result
         */
        if (src_exponent >= 0) {
                /* 
                 * Check sign.
                 * If negative, trap unimplemented.
                 */
                if (Dbl_isone_sign(srcp1)) {
                        resultp1 = resultp2 = 0;
                        if (Is_invalidtrap_enabled()) {
                                return(INVALIDEXCEPTION);
                        }
                        Set_invalidflag();
                        Duint_copytoptr(resultp1,resultp2,dstptr);
                        return(NOEXCEPTION);
                }
                Dbl_clear_signexponent_set_hidden(srcp1);
                Duint_from_dbl_mantissa(srcp1,srcp2,src_exponent,resultp1,
                  resultp2);

                /* check for inexact */
                if (Dbl_isinexact_to_unsigned(srcp1,srcp2,src_exponent)) {
                        inexact = TRUE;
                        /*  round result  */
                        switch (Rounding_mode()) {
                        case ROUNDPLUS:
                                Duint_increment(resultp1,resultp2);
                                break;
                        case ROUNDMINUS: /* never negative */
                                break;
                        case ROUNDNEAREST:
                                if(Dbl_isone_roundbit(srcp1,srcp2,src_exponent))
                                  if(Dbl_isone_stickybit(srcp1,srcp2,src_exponent) || 
                                     Duint_isone_lowp2(resultp2))
                                        Duint_increment(resultp1,resultp2);
                        } 
                }
        } else {
                Duint_setzero(resultp1,resultp2);

                /* check for inexact */
                if (Dbl_isnotzero_exponentmantissa(srcp1,srcp2)) {
                        inexact = TRUE;
                        /*  round result  */
                        switch (Rounding_mode()) {
                        case ROUNDPLUS:
                                if (Dbl_iszero_sign(srcp1)) {
                                        Duint_increment(resultp1,resultp2);
                                }
                                break;
                        case ROUNDMINUS:
                                if (Dbl_isone_sign(srcp1)) {
                                        resultp1 = resultp2 = 0;
                                        if (Is_invalidtrap_enabled()) {
                                                return(INVALIDEXCEPTION);
                                        }
                                        Set_invalidflag();
                                        inexact = FALSE;
                                }
                                break;
                        case ROUNDNEAREST:
                                if (src_exponent == -1 &&
                                    Dbl_isnotzero_mantissa(srcp1,srcp2))
                                        if (Dbl_iszero_sign(srcp1)) {
                                                Duint_increment(resultp1,resultp2);
                                        } else {
                                                resultp1 = 0;
                                                resultp2 = 0;
                                                if (Is_invalidtrap_enabled()) {
                                                        return(INVALIDEXCEPTION);
                                                }
                                                Set_invalidflag();
                                                inexact = FALSE;
                                        }
                        }
                }
        }
        Duint_copytoptr(resultp1,resultp2,dstptr);
        if (inexact) {
                if (Is_inexacttrap_enabled()) return(INEXACTEXCEPTION);
                else Set_inexactflag();
        }
        return(NOEXCEPTION);
}