/*
FUNCTION
        <<strtoufix16>>, <<strtoufix32>>, <<strtoufix64>>---string to signed fixed point

INDEX
	strtoufix16
INDEX
	strtoufix32
INDEX
	strtoufix64
INDEX
	_strtoufix16_r
INDEX
	_strtoufix32_r
INDEX
	_strtoufix64_r

SYNOPSIS
	#include <stdlib.h>
        __uint16_t strtoufix16 (const char *<[s]>, char **<[ptr]>);

        __uint32_t strtoufix32 (const char *<[s]>, char **<[ptr]>);

        __uint64_t strtoufix64 (const char *<[s]>, char **<[ptr]>);

        __uint16_t _strtoufix16_r (void *<[reent]>, 
                       const char *<[s]>, char **<[ptr]>);

        __uint32_t _strtoufix32_r (void *<[reent]>, 
                       const char *<[s]>, char **<[ptr]>);

        __uint64_t _strtoufix64_r (void *<[reent]>, 
                       const char *<[s]>, char **<[ptr]>);

DESCRIPTION
        The function <<strtoufix16>> converts the string <<*<[s]>>> to
	a fixed-point 16-bits fraction representation.  The function 
	follows the same rules as <<strtod>>.

	The substring converted is the longest initial
	subsequence of <[s]>, beginning with the first
	non-whitespace character, that has the format:
	.[+|-]<[digits]>[.][<[digits]>][(e|E)[+|-]<[digits]>] 
	The substring contains no characters if <[s]> is empty, consists
	entirely of whitespace, or if the first non-whitespace
	character is something other than <<+>>, <<->>, <<.>>, or a
	digit. If the substring is empty, no conversion is done, and
	the value of <[s]> is stored in <<*<[ptr]>>>.  Otherwise,
	the substring is converted, and a pointer to the final string
	(which will contain at least the terminating null character of
	<[s]>) is stored in <<*<[ptr]>>>.  If you want no
	assignment to <<*<[ptr]>>>, pass a null pointer as <[ptr]>.

	<<strtoufix32>> is identical to <<strtoufix16>> except that it 
	converts to fixed-point 32-bit fraction representation.
	<<strtoufix64>> is also similar, except that it converts
	to fixed-point 64-bit fraction.

	The alternate functions <<_strtoufix16_r>>, <<_strtoufix32_r>>,
	and <<_strtoufix64_r>> are reentrant versions.
	The extra argument <[reent]> is a pointer to a reentrancy structure.

RETURNS
	The functions return the converted substring value, if any.  If
	no conversion can be performed, then 0 is returned.  If the converted
	value is a NaN, 0 is returned and errno is set to <<EDOM>>.
	If the converted value exceeds the maximum positive unsigned fixed-point value, 
	the output value is saturated to the maximum value and <<ERANGE>> is stored in 
	errno.  If the converted value is less than 0, then the output is saturated to 0
	and <<ERANGE>> is stored in errno.  Otherwise, the converted value is returned in the
	specified fixed-point format.

PORTABILITY
        <<strtoufix16>>, <<strtoufix32>>, and <<strtoufix64>> are non-standard.

        The OS subroutines of <<strtod>> are required.
*/

#ifdef __SPE__

#include <_ansi.h>
#include <limits.h>
#include <errno.h>
#include <stdlib.h>
#include <reent.h>
#include "vfieeefp.h"

/*
 * Convert a string to a fixed-point 16-bit value.
 *
 * Ignores `locale' stuff.
 */
__uint16_t
_strtoufix16_r (struct _reent *rptr,
	const char *nptr,
	char **endptr)
{
  union double_union dbl;
  unsigned long tmp, tmp2, result;
  int exp, negexp;

  dbl.d = _strtod_r (rptr, nptr, endptr);

  /* treat NAN as domain error, +/- infinity as saturation */
  if (!finite(dbl.d))
    {
      if (isnan (dbl.d))
	{
	  _REENT_ERRNO(rptr) = EDOM;
	  return 0;
	}
      _REENT_ERRNO(rptr) = ERANGE;
      if (word0(dbl) & Sign_bit)
	return 0;
      return USHRT_MAX;
    }

  /* check for normal saturation */
  if (dbl.d >= 1.0)
    {
      _REENT_ERRNO(rptr) = ERANGE;
      return USHRT_MAX;
    }
  else if (dbl.d < 0)
    {
      _REENT_ERRNO(rptr) = ERANGE;
      return 0;
    }

  /* otherwise we have normal postive number in range */

  /* strip off exponent */
  exp = ((word0(dbl) & Exp_mask) >> Exp_shift) - Bias;
  negexp = -exp;
  if (negexp > 16)
    return 0;
  /* add in implicit normalized bit */
  tmp = word0(dbl) | Exp_msk1;
  /* remove exponent and sign */
  tmp <<= Ebits;
  /* perform rounding */
  tmp2 = tmp + (1 << (negexp + 14));
  result = tmp2 >> (negexp + 15);
  /* if rounding causes carry, must add carry bit in */
  if (tmp2 < tmp)
    {
      if (negexp == 0)
	{
	  /* we have overflow which means saturation */
	  _REENT_ERRNO(rptr) = ERANGE;
	  return USHRT_MAX;
	}
      result |= (1 << (16 - negexp));
    }

  return (__uint16_t)result;
}

#ifndef _REENT_ONLY

__uint16_t
strtoufix16 (const char *s,
	char **ptr)
{
  return _strtoufix16_r (_REENT, s, ptr);
}

#endif

#endif /* __SPE__ */
