/* $Id: float.h,v 1.5 2009/08/29 21:19:43 fredette Exp $ */

/* tme/generic/float.h - public header file for floating-point emulation */

/*
 * Copyright (c) 2004 Matt Fredette
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Matt Fredette.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _TME_GENERIC_FLOAT_H
#define _TME_GENERIC_FLOAT_H

#include <tme/common.h>
_TME_RCSID("$Id: float.h,v 1.5 2009/08/29 21:19:43 fredette Exp $");

/* includes: */
#include <tme/threads.h>
#include <setjmp.h>
#include <math.h>
#ifdef _TME_HAVE_IEEEFP_H
#include <ieeefp.h>
#endif /* _TME_HAVE_IEEEFP_H */
#ifdef _TME_HAVE_FLOAT_H
#include <float.h>
#endif /* _TME_HAVE_FLOAT_H */
#ifdef _TME_HAVE_LIMITS_H
#include <limits.h>
#endif /* _TME_HAVE_LIMITS_H */

/* macros: */

/* floating-point formats.  even though these are individual bits, we
   can't use TME_BIT() because these are used in preprocessor ifs: */
#define TME_FLOAT_FORMAT_NULL				(0)
#ifndef TME_FLOAT_FORMAT_FLOAT
#define TME_FLOAT_FORMAT_FLOAT				(1)
#endif /* !TME_FLOAT_FORMAT_FLOAT */
#ifndef TME_FLOAT_FORMAT_DOUBLE
#define TME_FLOAT_FORMAT_DOUBLE				(2)
#endif /* !TME_FLOAT_FORMAT_FLOAT */
#ifdef _TME_HAVE_LONG_DOUBLE
#ifndef TME_FLOAT_FORMAT_LONG_DOUBLE
#define TME_FLOAT_FORMAT_LONG_DOUBLE			(4)
#endif /* !TME_FLOAT_FORMAT_LONG_DOUBLE */
#endif /* _TME_HAVE_LONG_DOUBLE */
#define TME_FLOAT_FORMAT_IEEE754_SINGLE			(8)
#define TME_FLOAT_FORMAT_IEEE754_DOUBLE			(16)
#define TME_FLOAT_FORMAT_IEEE754_EXTENDED80_I387	(32)
#define TME_FLOAT_FORMAT_IEEE754_EXTENDED80_M68881	(64)
#define TME_FLOAT_FORMAT_IEEE754_QUAD			(128)

/* if the long double type is available, this expands to the code
   fragment in x, else it expands to nothing: */
#ifdef _TME_HAVE_LONG_DOUBLE
#define TME_FLOAT_IF_LONG_DOUBLE(x) x
#else  /* !_TME_HAVE_LONG_DOUBLE */
#define TME_FLOAT_IF_LONG_DOUBLE(x) /* */
#endif /* !_TME_HAVE_LONG_DOUBLE */

/* a mask of all builtin floating-point formats: */
#define TME_FLOAT_FORMATS_BUILTIN			(TME_FLOAT_FORMAT_FLOAT				\
							 | TME_FLOAT_FORMAT_DOUBLE			\
							 TME_FLOAT_IF_LONG_DOUBLE(| TME_FLOAT_FORMAT_LONG_DOUBLE))

/* floating-point rounding modes: */
#ifdef _TME_HAVE_FPSETROUND
#define TME_FLOAT_ROUND_NULL			(0xdeadbeef)
#define TME_FLOAT_ROUND_NEAREST_EVEN		(FP_RN)
#define TME_FLOAT_ROUND_DOWN			(FP_RM)
#define TME_FLOAT_ROUND_UP			(FP_RP)
#define TME_FLOAT_ROUND_TO_ZERO			(FP_RZ)
#else  /* !_TME_HAVE_FPSETROUND */
#define TME_FLOAT_ROUND_NULL			(0)
#define TME_FLOAT_ROUND_NEAREST_EVEN		(1)
#define TME_FLOAT_ROUND_DOWN			(2)
#define TME_FLOAT_ROUND_UP			(3)
#define TME_FLOAT_ROUND_TO_ZERO			(4)
#endif /* !_TME_HAVE_FPSETROUND */

/* floating-point exceptions: */
#define TME_FLOAT_EXCEPTION_GENERIC		TME_BIT(0)
#define TME_FLOAT_EXCEPTION_INVALID		TME_BIT(1)
#define TME_FLOAT_EXCEPTION_DIVBYZERO		TME_BIT(2)
#define TME_FLOAT_EXCEPTION_OVERFLOW		TME_BIT(3)
#define TME_FLOAT_EXCEPTION_UNDERFLOW		TME_BIT(4)
#define TME_FLOAT_EXCEPTION_INEXACT		TME_BIT(5)
#define TME_FLOAT_EXCEPTION_OVERFLOW_INT	TME_BIT(6)
#define TME_FLOAT_EXCEPTION_DENORMAL		TME_BIT(7)

/* types: */

/* each of these types describes a floating point format supported on
   hosts.  each of these types may also be used in code that emulates
   the a floating point format, but each type's first purpose is to
   overlap exactly with one or more of the host's builtin C types that
   uses the format.

   for example, if the host has a builtin C type that uses the i387
   style of the IEEE 754 80-bit extended precision format, struct
   tme_float_ieee754_extended80 matches that style, not the m68k
   style.  if no builtin C type uses any style of the IEEE 754 80-bit
   extended precision format, struct tme_float_ieee754_extended80
   matches a random style.

   member values in these structures are always in host byte and word
   order.

   we also allow for these parts of the header file to be multiply
   included, so that these types can be incorporated into emulation
   code under different names: */

/* tme_uint32_t is used for the IEEE 754 single precision format. */

/* union tme_value64 is used for the IEEE 754 double precision format. */

#undef _TME_GENERIC_FLOAT_H
#endif /* !_TME_GENERIC_FLOAT_H */

#ifndef TME_FLOAT_FORMAT_IEEE754_EXTENDED80

/* making the IEEE 754 80-bit extended precision float structure
   overlap with a native type is complicated, since there are four
   possible representations: i387-style (where the 16 bits of sign and
   exponent are adjacent to the significand) and m68881-style (where
   there is a 16-bit gap between the significand and the 16 bits of
   sign and exponent), plus big- and little-endian variants of each: */

/* decide where the 16-bit sign-exponent word and any 16-bit padding
   words are in relation to the significand: */
#if ((TME_FLOAT_FORMATS_BUILTIN & TME_FLOAT_FORMAT_IEEE754_EXTENDED80_M68881) != 0)
#define TME_FLOAT_FORMAT_IEEE754_EXTENDED80	TME_FLOAT_FORMAT_IEEE754_EXTENDED80_M68881
#define _tme_float_ieee754_extended80_word_1 tme_float_ieee754_extended80_sexp
#define _tme_float_ieee754_extended80_word_0 tme_float_ieee754_extended80_pad
#else  /* ((TME_FLOAT_FORMATS_BUILTIN & TME_FLOAT_FORMAT_IEEE754_EXTENDED80_M68881) == 0) */
#define TME_FLOAT_FORMAT_IEEE754_EXTENDED80	TME_FLOAT_FORMAT_IEEE754_EXTENDED80_I387
#define _tme_float_ieee754_extended80_word_0 tme_float_ieee754_extended80_sexp
#endif /* ((TME_FLOAT_FORMATS_BUILTIN & TME_FLOAT_FORMAT_IEEE754_EXTENDED80_M68881) == 0) */

/* the IEEE 754 80-bit extended precision format: */
struct tme_float_ieee754_extended80 {

  /* if this is a big-endian host: */
#ifdef _TME_WORDS_BIGENDIAN

  /* up to four 16-bit words preceding the significand: */
#ifdef _tme_float_ieee754_extended80_word_3
  tme_uint16_t _tme_float_ieee754_extended80_word_3;
#endif /* _tme_float_ieee754_extended80_word_3 */
#ifdef _tme_float_ieee754_extended80_word_2
  tme_uint16_t _tme_float_ieee754_extended80_word_2;
#endif /* _tme_float_ieee754_extended80_word_2 */
#ifdef _tme_float_ieee754_extended80_word_1
  tme_uint16_t _tme_float_ieee754_extended80_word_1;
#endif /* _tme_float_ieee754_extended80_word_1 */
  tme_uint16_t _tme_float_ieee754_extended80_word_0;

#endif /* _TME_WORDS_BIGENDIAN */

  /* the significand: */
#ifndef tme_float_ieee754_extended80
  union tme_value64 tme_float_ieee754_extended80_significand;
#else  /* !tme_float_ieee754_extended80 */
  tme_uint64_t tme_float_ieee754_extended80_significand;
#endif /* !tme_float_ieee754_extended80 */

  /* if this is a little-endian host: */
#ifndef _TME_WORDS_BIGENDIAN

  /* up to four 16-bit words following the significand: */
  tme_uint16_t _tme_float_ieee754_extended80_word_0;
#ifdef _tme_float_ieee754_extended80_word_1
  tme_uint16_t _tme_float_ieee754_extended80_word_1;
#endif /* _tme_float_ieee754_extended80_word_1 */
#ifdef _tme_float_ieee754_extended80_word_2
  tme_uint16_t _tme_float_ieee754_extended80_word_2;
#endif /* _tme_float_ieee754_extended80_word_2 */
#ifdef _tme_float_ieee754_extended80_word_3
  tme_uint16_t _tme_float_ieee754_extended80_word_3;
#endif /* _tme_float_ieee754_extended80_word_3 */

#endif /* !_TME_WORDS_BIGENDIAN */
#undef _tme_float_ieee754_extended80_word_0
#undef _tme_float_ieee754_extended80_word_1
#undef _tme_float_ieee754_extended80_word_2
#undef _tme_float_ieee754_extended80_word_3
};

#endif /* TME_FLOAT_FORMAT_IEEE754_EXTENDED80 */

#ifndef _TME_GENERIC_FLOAT_H
#define _TME_GENERIC_FLOAT_H

/* the IEEE 754 quad precision format: */
struct tme_float_ieee754_quad {
#ifdef _TME_WORDS_BIGENDIAN
  union tme_value64 tme_float_ieee754_quad_hi;
  union tme_value64 tme_float_ieee754_quad_lo;
#else  /* !_TME_WORDS_BIGENDIAN */
  union tme_value64 tme_float_ieee754_quad_lo;
  union tme_value64 tme_float_ieee754_quad_hi;
#endif /* !_TME_WORDS_BIGENDIAN */
};

/* the generic float: */
struct tme_float {

  /* the format of this float: */
  unsigned int tme_float_format;

  /* the value of this float: */
  union {
    
    /* the builtin formats: */
    float _tme_float_union_float;
    double _tme_float_union_double;
#ifdef _TME_HAVE_LONG_DOUBLE
    long double _tme_float_union_long_double;
#endif /* _TME_HAVE_LONG_DOUBLE */

    /* the IEEE 754 formats: */
    tme_uint32_t _tme_float_union_ieee754_single;
    union tme_value64 _tme_float_union_ieee754_double;
    struct tme_float_ieee754_extended80 _tme_float_union_ieee754_extended80;
    struct tme_float_ieee754_quad _tme_float_union_ieee754_quad;
  } _tme_float_union;

  /* the public member names: */
#define tme_float_value_float _tme_float_union._tme_float_union_float
#define tme_float_value_double _tme_float_union._tme_float_union_double
#ifdef _TME_HAVE_LONG_DOUBLE
#define tme_float_value_long_double _tme_float_union._tme_float_union_long_double
#endif /* _TME_HAVE_LONG_DOUBLE */
#define tme_float_value_ieee754_single _tme_float_union._tme_float_union_ieee754_single
#define tme_float_value_ieee754_double _tme_float_union._tme_float_union_ieee754_double
#define tme_float_value_ieee754_extended80 _tme_float_union._tme_float_union_ieee754_extended80
#define tme_float_value_ieee754_quad _tme_float_union._tme_float_union_ieee754_quad
};

/* prototypes: */

/* this enters native floating-point operation: */
void tme_float_enter _TME_P((int, void (*)(int, void *), void *));

/* this returns the current native floating-point exceptions: */
int tme_float_exceptions _TME_P((void));

/* this leaves native floating-point operation: */
int tme_float_leave _TME_P((void));

/* this asserts that the float is in one of the expected formats: */
#ifndef NDEBUG
static _tme_inline int
tme_float_assert_formats(_tme_const struct tme_float *x, unsigned int formats)
{
  assert (x->tme_float_format & formats);
  return (TRUE);
}
#else  /* NDEBUG */
#define tme_float_assert_formats(x, formats) (TRUE)
#endif /* NDEBUG */

/* this evaluates to nonzero if the float is in a certain format, if
   the float is known to be one of the given formats: */
#define tme_float_is_format(x, formats, format)	\
  (((formats) == (format)) || (((formats) & (format)) && ((x)->tme_float_format & (format))))

/* this sets a float to a given builtin value: */
#define tme_float_value_builtin_set(x, format, y) \
  do { \
    if ((format) == TME_FLOAT_FORMAT_FLOAT) { \
      (x)->tme_float_value_float = (y); \
    } \
    TME_FLOAT_IF_LONG_DOUBLE(else if ((format) == TME_FLOAT_FORMAT_LONG_DOUBLE) { \
      (x)->tme_float_value_long_double = (y); \
    }) \
    else { \
      assert((format) == TME_FLOAT_FORMAT_DOUBLE); \
      (x)->tme_float_value_double = (y); \
    } \
    (x)->tme_float_format = (format); \
  } while (/* CONSTCOND */ 0)

/* these return the exponents of values in the IEEE 754 formats: */
#define tme_float_value_ieee754_exponent_single(x) \
  TME_FIELD_MASK_EXTRACTU((x)->tme_float_value_ieee754_single, 0x7f800000)
#define tme_float_value_ieee754_exponent_double(x) \
  TME_FIELD_MASK_EXTRACTU((x)->tme_float_value_ieee754_double.tme_value64_uint32_hi, 0x7ff00000)
#define tme_float_value_ieee754_exponent_extended80(x) \
  TME_FIELD_MASK_EXTRACTU((x)->tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_sexp, 0x7fff)
#define tme_float_value_ieee754_exponent_quad(x) \
  TME_FIELD_MASK_EXTRACTU((x)->tme_float_value_ieee754_quad.tme_float_ieee754_quad_hi.tme_value64_uint32_hi, 0x7fff0000)

/* these return a bitwise-or of the fraction bits in values in the
   IEEE 754 formats (and, for the IEEE 754 80-bit extended precision
   format, a bitwise-or of the entire significand, including the
   explicit integer bit): */
#define tme_float_value_ieee754_fracor_single(x) \
  ((x)->tme_float_value_ieee754_single & 0x007fffff)
#define tme_float_value_ieee754_fracor_double(x) \
  (((x)->tme_float_value_ieee754_double.tme_value64_uint32_hi & 0x000fffff) | (x)->tme_float_value_ieee754_double.tme_value64_uint32_lo)
#define tme_float_value_ieee754_fracor_extended80(x) \
  (((x)->tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_hi << 1) | (x)->tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_lo)
#define tme_float_value_ieee754_sigor_extended80(x) \
  ((x)->tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_hi | (x)->tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_significand.tme_value64_uint32_lo)
#define tme_float_value_ieee754_fracor_quad(x) \
  (((x)->tme_float_value_ieee754_quad.tme_float_ieee754_quad_hi.tme_value64_uint32_hi & 0x0000ffff) \
   | (x)->tme_float_value_ieee754_quad.tme_float_ieee754_quad_hi.tme_value64_uint32_lo \
   | (x)->tme_float_value_ieee754_quad.tme_float_ieee754_quad_lo.tme_value64_uint32_hi \
   | (x)->tme_float_value_ieee754_quad.tme_float_ieee754_quad_lo.tme_value64_uint32_lo)

/* this evaluates to nonzero if the float is a NaN: */
#define tme_float_is_nan(x, formats) \
  (tme_float_assert_formats(x, formats) \
   && (tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_SINGLE) \
       ? (tme_float_value_ieee754_exponent_single(x) == 0xff \
	  && tme_float_value_ieee754_fracor_single(x) != 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_DOUBLE) \
       ? (tme_float_value_ieee754_exponent_double(x) == 0x7ff \
	  && tme_float_value_ieee754_fracor_double(x) != 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_EXTENDED80) \
       ? (tme_float_value_ieee754_exponent_extended80(x) == 0x7fff \
	  && tme_float_value_ieee754_fracor_extended80(x) != 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_QUAD) \
       ? (tme_float_value_ieee754_exponent_quad(x) == 0x7fff \
	  && tme_float_value_ieee754_fracor_quad(x) != 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_FLOAT) \
       ? isnan((x)->tme_float_value_float) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_DOUBLE) \
       ? isnan((x)->tme_float_value_double) \
       : TME_FLOAT_IF_LONG_DOUBLE(isnan((x)->tme_float_value_long_double) ||) FALSE))

/* this evaluates to nonzero if the float is an infinity: */
#define tme_float_is_inf(x, formats) \
  (tme_float_assert_formats(x, formats) \
   && (tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_SINGLE) \
       ? (tme_float_value_ieee754_exponent_single(x) == 0xff \
	  && tme_float_value_ieee754_fracor_single(x) == 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_DOUBLE) \
       ? (tme_float_value_ieee754_exponent_double(x) == 0x7ff \
	  && tme_float_value_ieee754_fracor_double(x) == 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_EXTENDED80) \
       ? (tme_float_value_ieee754_exponent_extended80(x) == 0x7fff \
	  && tme_float_value_ieee754_fracor_extended80(x) == 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_QUAD) \
       ? (tme_float_value_ieee754_exponent_quad(x) == 0x7fff \
	  && tme_float_value_ieee754_fracor_quad(x) == 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_FLOAT) \
       ? isinf((x)->tme_float_value_float) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_DOUBLE) \
       ? isinf((x)->tme_float_value_double) \
       : TME_FLOAT_IF_LONG_DOUBLE(isinf((x)->tme_float_value_long_double) ||) FALSE))

/* this evaluates to nonzero if the float is a zero: */
#define tme_float_is_zero(x, formats) \
  (tme_float_assert_formats(x, formats) \
   && (tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_SINGLE) \
       ? (tme_float_value_ieee754_exponent_single(x) == 0 \
	  && tme_float_value_ieee754_fracor_single(x) == 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_DOUBLE) \
       ? (tme_float_value_ieee754_exponent_double(x) == 0 \
	  && tme_float_value_ieee754_fracor_double(x) == 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_EXTENDED80) \
       ? (tme_float_value_ieee754_exponent_extended80(x) == 0 \
	  && tme_float_value_ieee754_sigor_extended80(x) == 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_QUAD) \
       ? (tme_float_value_ieee754_exponent_quad(x) == 0 \
	  && tme_float_value_ieee754_fracor_quad(x) == 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_FLOAT) \
       ? ((x)->tme_float_value_float == 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_DOUBLE) \
       ? ((x)->tme_float_value_double == 0) \
       : TME_FLOAT_IF_LONG_DOUBLE(((x)->tme_float_value_long_double == 0) ||) FALSE))

/* this evaluates to nonzero if the float is negative: */
#define tme_float_is_negative(x, formats) \
  (tme_float_assert_formats(x, formats) \
   && (tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_SINGLE) \
       ? (((x)->tme_float_value_ieee754_single & 0x80000000) != 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_DOUBLE) \
       ? (((x)->tme_float_value_ieee754_double.tme_value64_uint32_hi & 0x80000000) != 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_EXTENDED80) \
       ? (((x)->tme_float_value_ieee754_extended80.tme_float_ieee754_extended80_sexp & 0x8000) != 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_IEEE754_QUAD) \
       ? (((x)->tme_float_value_ieee754_quad.tme_float_ieee754_quad_hi.tme_value64_uint32_hi & 0x80000000) != 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_FLOAT) \
       ? ((x)->tme_float_value_float < 0) \
       : tme_float_is_format(x, formats, TME_FLOAT_FORMAT_DOUBLE) \
       ? ((x)->tme_float_value_double < 0) \
       : TME_FLOAT_IF_LONG_DOUBLE(((x)->tme_float_value_long_double < 0) ||) FALSE))

/* if possible, this returns a positive or negative infinity
   float, otherwise, this returns the float value
   closest to that infinity: */
float tme_float_infinity_float _TME_P((int));

/* if possible, this returns a negative zero float.
   otherwise, this returns the negative float value closest
   to zero: */
float tme_float_negative_zero_float _TME_P((void));

/* this returns the radix 2 mantissa and exponent of an in-range float.
   the mantissa is either zero, or in the range [1,2): */
float tme_float_radix2_mantissa_exponent_float _TME_P((float, tme_int32_t *));

/* this scales a value by adding n to its exponent: */
float tme_float_radix2_scale_float _TME_P((float, tme_int32_t));

/* this returns the radix 10 mantissa and exponent of an in-range float.
   the mantissa is either zero, or in the range [1,10): */
float tme_float_radix10_mantissa_exponent_float _TME_P((float, tme_int32_t *));

/* this scales a value by adding n to its exponent: */
float tme_float_radix10_scale_float _TME_P((float, tme_int32_t));

/* if possible, this returns a positive or negative infinity
   double, otherwise, this returns the double value
   closest to that infinity: */
double tme_float_infinity_double _TME_P((int));

/* if possible, this returns a negative zero double.
   otherwise, this returns the negative double value closest
   to zero: */
double tme_float_negative_zero_double _TME_P((void));

/* this returns the radix 2 mantissa and exponent of an in-range double.
   the mantissa is either zero, or in the range [1,2): */
double tme_float_radix2_mantissa_exponent_double _TME_P((double, tme_int32_t *));

/* this scales a value by adding n to its exponent: */
double tme_float_radix2_scale_double _TME_P((double, tme_int32_t));

/* this returns the radix 10 mantissa and exponent of an in-range double.
   the mantissa is either zero, or in the range [1,10): */
double tme_float_radix10_mantissa_exponent_double _TME_P((double, tme_int32_t *));

/* this scales a value by adding n to its exponent: */
double tme_float_radix10_scale_double _TME_P((double, tme_int32_t));

#ifdef _TME_HAVE_LONG_DOUBLE

/* if possible, this returns a positive or negative infinity
   long double, otherwise, this returns the long double value
   closest to that infinity: */
long double tme_float_infinity_long_double _TME_P((int));

/* if possible, this returns a negative zero long double.
   otherwise, this returns the negative long double value closest
   to zero: */
long double tme_float_negative_zero_long_double _TME_P((void));

/* this returns the radix 2 mantissa and exponent of an in-range long double.
   the mantissa is either zero, or in the range [1,2): */
long double tme_float_radix2_mantissa_exponent_long_double _TME_P((long double, tme_int32_t *));

/* this scales a value by adding n to its exponent: */
long double tme_float_radix2_scale_long_double _TME_P((long double, tme_int32_t));

/* this returns the radix 10 mantissa and exponent of an in-range long double.
   the mantissa is either zero, or in the range [1,10): */
long double tme_float_radix10_mantissa_exponent_long_double _TME_P((long double, tme_int32_t *));

/* this scales a value by adding n to its exponent: */
long double tme_float_radix10_scale_long_double _TME_P((long double, tme_int32_t));

#endif /* _TME_HAVE_LONG_DOUBLE */

/* prototypes for missing standard functions: */
#ifndef isnan
# define isnan(x)						 \
  (sizeof (x) == sizeof (long double) ? isnan_ld (x)		 \
   : sizeof (x) == sizeof (double) ? isnan_d (x)		 \
   : isnan_f (x))
static inline int isnan_f  _TME_P((float       x)) { return x != x; }
static inline int isnan_d  _TME_P((double      x)) { return x != x; }
static inline int isnan_ld _TME_P((long double x)) { return x != x; }
#endif

#ifndef isinf
# define isinf(x)						 \
  (sizeof (x) == sizeof (long double) ? isinf_ld (x)		 \
   : sizeof (x) == sizeof (double) ? isinf_d (x)		 \
   : isinf_f (x))
static inline int isinf_f  _TME_P((float       x))
{ return !isnan (x) && isnan (x - x); }
static inline int isinf_d  _TME_P((double      x))
{ return !isnan (x) && isnan (x - x); }
static inline int isinf_ld _TME_P((long double x))
{ return !isnan (x) && isnan (x - x); }
#endif

#endif /* _TME_GENERIC_FLOAT_H */
