fenv.c   [plain text]


/*
 *  fenv.c
 *  cLibm
 *
 *  Created by Ian Ollmann on 7/15/07.
 *  Copyright 2007 Apple Inc. All rights reserved.
 *
 *	A partial implementation for Intel is provided here for testing purposes. 
 *	You'll need to provide a full implementation for your platform of choice.
 *
 */

#if defined(ARMLIBM_FENV_SUPPORT)

#include "fenv.h"
#include <stdint.h>
#include "required_arithmetic.h"

#define FE_ALL_RND  ( FE_TONEAREST | FE_TOWARDZERO | FE_UPWARD | FE_DOWNWARD )

#if defined( __GNUC__ )
	#define ALWAYS_INLINE __attribute__ ((__always_inline__))
#else
	#define ALWAYS_INLINE
#endif

#ifndef __arm__
	#error	This file is for use on ARM + VFP and later
#endif


#define GET_FPSCR()			({ uint32_t _fpscr; __asm__ __volatile__( "fmrx	%0, fpscr" : "=r" (_fpscr) ); /* return */ _fpscr; })
#define SET_FPSCR(_fpscr)	__asm__ __volatile__( "fmxr	fpscr, %0" : : "r" (_fpscr)  )


const fenv_t _FE_DFL_ENV = {{{ 0, 0, 0, 0 }}};
// Worker functions for making sure that much of the work is done in a uniform way

// For each bit set in excepts, change the floating point state to match the correspinding bit in *flagp
static inline int _fesetexceptflag_private(const fexcept_t *flagp, int excepts ) ALWAYS_INLINE;
static inline int _fesetexceptflag_private(const fexcept_t *flagp, int excepts )
{
	uint32_t fpscr = GET_FPSCR();
	
	excepts &=  FE_ALL_EXCEPT | 0x8000;
	
	fpscr &= ~excepts;
	fpscr |= *flagp & excepts;
	
	SET_FPSCR( fpscr );
	
	return 0;
}

// For each bit set in excepts, copy the corresponding bit from the floating point state into *flagp
//	All other bits shall be set to zero
static inline int _fegetexceptflag_private(fexcept_t *flagp, int excepts);
static inline int _fegetexceptflag_private(fexcept_t *flagp, int excepts)
{
	uint32_t fpscr = GET_FPSCR();
	
	*flagp = fpscr & excepts;
	
	return 0;
}

                                                    

/*******************************************************************************
*     The function "feclearexcept" clears the supported floating point         *
*     exceptions represented by its argument.                                  *
*******************************************************************************/

int  feclearexcept(int excepts)
{
    fexcept_t zero = 0;
    return _fesetexceptflag_private( &zero, excepts );
}



/*******************************************************************************
*     The function "feraiseexcept" raises the supported floating-point         *
*     exceptions represented by its argument. The order in which these         *
*     floating-point exceptions are raised is unspecified.                     *
*******************************************************************************/

int  feraiseexcept(int excepts)
{
	int inexact_set = 0;

	if( excepts & FE_OVERFLOW )
	{
		required_add_float( 0x1.0p127f, 0x1.0p127f );
		inexact_set = 1;
	}
	if( excepts & FE_UNDERFLOW )
	{
		required_multiply_float( 0x1.0p-126f, 0x1.0p-126f );
		inexact_set = 1;
	}
	if( excepts & FE_INVALID )
		required_add_float( __builtin_inff(), -__builtin_inff() );
	if( excepts & FE_DIVBYZERO )
		required_divide_float( 1.0f, 0.0f );
	if( 0 != (excepts & FE_INEXACT) && 0 == inexact_set )
		required_add_float( 0x1.0p127f, 1.0f );

	return 0;
}






/*******************************************************************************
*     The function "fetestexcept" determines which of the specified subset of  *
*     the floating-point exception flags are currently set.  The excepts       *
*     argument specifies the floating-point status flags to be queried. This   *
*     function returns the value of the bitwise OR of the floating-point       *
*     exception macros corresponding to the currently set floating-point       *
*     exceptions included in excepts.                                          *
*                                                                              *
*******************************************************************************/

int  fetestexcept(int excepts )
{
	fexcept_t	t = 0;

	_fegetexceptflag_private( &t, excepts);

	return t;
}


/*******************************************************************************
*     The following functions provide control of rounding direction modes.     *
*******************************************************************************/

/*******************************************************************************
*     The function "fegetround" returns the value of the rounding direction    *
*     macro which represents the current rounding direction, or a negative     *
*     if there is no such rounding direction macro or the current rounding     *
*     direction is not determinable.                                           *
*******************************************************************************/

int  fegetround(void)
{
	int32_t	fpscr = GET_FPSCR();
	
	return fpscr & FE_ALL_RND;
}


/*******************************************************************************
*     The function "fesetround" establishes the rounding direction represented *
*     by its argument "round". If the argument is not equal to the value of a  *
*     rounding direction macro, the rounding direction is not changed.  It     *
*     returns zero if and only if the argument is equal to a rounding          *
*     direction macro.                                                         *
*******************************************************************************/

int  fesetround(int round )
{
	if( (round & FE_ALL_RND) != round )
		return round;

	int32_t fpscr = GET_FPSCR();

	fpscr &= ~FE_ALL_RND;
	fpscr |= round & FE_ALL_RND;
	 
	SET_FPSCR( fpscr );
	
	return 0;
}


/*******************************************************************************
*    The following functions manage the floating-point environment, exception  *
*    flags and dynamic modes, as one entity.                                   *
*******************************************************************************/

/*******************************************************************************
*    The fegetenv function stores the current floating-point enviornment in    *
*    the object pointed to by envp.                                            *
*******************************************************************************/
int  fegetenv(fenv_t *envp)
{
	envp->__fpscr = GET_FPSCR();
	envp->__reserved0 = 0;
	envp->__reserved1 = 0;
	envp->__reserved2 = 0;

	return 0;
}

/*******************************************************************************
*    The feholdexcept function saves the current floating-point environment in *
*    the object pointed to by envp, clears the floating-point status flags,    *
*    and then installs a non-stop (continue on floating-point exceptions)      *
*    mode, if available, for all floating-point exceptions. The feholdexcept   *
*    function returns zero if and only if non-stop floating-point exceptions   *
*    handling was successfully installed.                                      *
*******************************************************************************/
int   feholdexcept(fenv_t *envp)
{
	uint32_t fpscr = GET_FPSCR();

	envp->__fpscr = fpscr;
	envp->__reserved0 = 0;
	envp->__reserved1 = 0;
	envp->__reserved2 = 0;

	fpscr &=  ~( FE_ALL_EXCEPT | (FE_ALL_EXCEPT << 8) );

	SET_FPSCR( fpscr );

	return 0;
}


/*******************************************************************************
*    The fesetnv function establishes the floating-point environment           *
*    represented by the object pointed to by envp. The argument envp shall     *
*    point to an object set by a call to fegetenv or feholdexcept, or equal to *
*    a floating-point environment macro -- we define only *FE_DFL_ENV and      *
*    FE_DISABLE_SSE_DENORMS_ENV -- to be C99 standard compliant and portable   *
*    to other architectures. Note that fesetnv merely installs the state of    *
*    the floating-point status flags represented through its argument, and     *
*    does not raise these floating-point exceptions.                           *
*                                                                              *
*******************************************************************************/
int  fesetenv(const fenv_t *envp)
{
	SET_FPSCR( envp->__fpscr );

	return 0;
}



/*******************************************************************************
*    The feupdateenv function saves the currently raised floating-point        *
*    exceptions in its automatic storage, installs the floating-point          *
*    environment represented by the object pointed to by envp, and then raises *
*    the saved floating-point exceptions. The argument envp shall point to an  *
*    object set by a call to feholdexcept or fegetenv or equal a               *
*    floating-point environment macro.                                         *
*                                                                              *
*******************************************************************************/
int  feupdateenv(const fenv_t *envp)
{
 	uint32_t oldenv = GET_FPSCR();
	
	SET_FPSCR( envp->__fpscr );
	
	int inexact_set = 0;

	if( oldenv & FE_OVERFLOW )
	{
		required_add_float( 0x1.0p127f, 0x1.0p127f );
		inexact_set = 1;
	}
	if( oldenv & FE_UNDERFLOW )
	{
		required_multiply_float( 0x1.0p-126f, 0x1.0p-126f );
		inexact_set = 1;
	}
	if( oldenv & FE_INVALID )
		required_add_float( __builtin_inff(), -__builtin_inff() );
	if( oldenv & FE_DIVBYZERO )
		required_divide_float( 1.0f, 0.0f );
	if( 0 != (oldenv & FE_INEXACT) && 0 == inexact_set )
		required_add_float( 0x1.0p127f, 1.0f );

	return 0;
}


/*******************************************************************************
*    The function "fegetexceptflag" stores a implementation-defined            *
*    representation of the states of the floating-point status flags indicated *
*    by its integer argument excepts in the object pointed to by the argument, * 
*    flagp.                                                                    *
*******************************************************************************/

int  fegetexceptflag(fexcept_t *flagp, int excepts)
{
	return _fegetexceptflag_private( flagp, excepts );
}
      
/*******************************************************************************
*     The function "fesetexceptflag" sets or clears the floating point status  *
*     flags indicated by the argument excepts to the states stored in the      *
*     object pointed to by flagp. The value of the *flagp shall have been set  *
*     by a previous call to fegetexceptflag whose second argument represented  *
*     at least those floating-point exceptions represented by the argument     *
*     excepts. This function does not raise floating-point exceptions; it just *
*     sets the state of the flags.                                             *
*******************************************************************************/

int  fesetexceptflag(const fexcept_t *flagp, int excepts )
{
	return _fesetexceptflag_private( flagp, excepts );
}

#endif