Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. #ifndef _ROUND_INTERNAL_H
  2. /*
  3.  * round_internal.h
  4.  *
  5.  * $Id: round_internal.h,v 1.1 2008/06/03 18:42:21 keithmarshall Exp $
  6.  *
  7.  * Provides a generic implementation of the numerical rounding
  8.  * algorithm, which is shared by all functions in the `round()',
  9.  * `lround()' and `llround()' families.
  10.  *
  11.  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
  12.  *
  13.  * This is free software.  You may redistribute and/or modify it as you
  14.  * see fit, without restriction of copyright.
  15.  *
  16.  * This software is provided "as is", in the hope that it may be useful,
  17.  * but WITHOUT WARRANTY OF ANY KIND, not even any implied warranty of
  18.  * MERCHANTABILITY, nor of FITNESS FOR ANY PARTICULAR PURPOSE.  At no
  19.  * time will the author accept any form of liability for any damages,
  20.  * however caused, resulting from the use of this software.
  21.  *
  22.  */
  23. #define _ROUND_INTERNAL_H
  24.  
  25. #include <math.h>
  26. #include <fenv.h>
  27.  
  28. #define TYPE_PASTE( NAME, TYPE )    NAME##TYPE
  29.  
  30. #define INPUT_TYPE                  INPUT_TYPEDEF( FUNCTION )
  31. #define INPUT_TYPEDEF( FUNCTION )   TYPE_PASTE( FUNCTION, _input_type )
  32. /*
  33.  * The types for the formal parameter, to each of the derived functions.
  34.  */
  35. #define round_input_type            double
  36. #define roundf_input_type           float
  37. #define roundl_input_type           long double
  38.  
  39. #define lround_input_type           double
  40. #define lroundf_input_type          float
  41. #define lroundl_input_type          long double
  42.  
  43. #define llround_input_type          double
  44. #define llroundf_input_type         float
  45. #define llroundl_input_type         long double
  46.  
  47. #define RETURN_TYPE                 RETURN_TYPEDEF( FUNCTION )
  48. #define RETURN_TYPEDEF( FUNCTION )  TYPE_PASTE( FUNCTION, _return_type )
  49. /*
  50.  * The types for the return value, from each of the derived functions.
  51.  */
  52. #define round_return_type           double
  53. #define roundf_return_type          float
  54. #define roundl_return_type          long double
  55.  
  56. #define lround_return_type          long
  57. #define lroundf_return_type         long
  58. #define lroundl_return_type         long
  59.  
  60. #define llround_return_type         long long
  61. #define llroundf_return_type        long long
  62. #define llroundl_return_type        long long
  63.  
  64. #define MAX_RETURN_VALUE            RETURN_MAX( FUNCTION )
  65. #define RETURN_MAX( FUNCTION )      TYPE_PASTE( FUNCTION, _return_max )
  66. /*
  67.  * The maximum values which may be returned by each of the derived functions
  68.  * in the `lround' or the `llround' families.
  69.  */
  70. #define lround_return_max           LONG_MAX
  71. #define lroundf_return_max          LONG_MAX
  72. #define lroundl_return_max          LONG_MAX
  73.  
  74. #define llround_return_max          LLONG_MAX
  75. #define llroundf_return_max         LLONG_MAX
  76. #define llroundl_return_max         LLONG_MAX
  77.  
  78. #define MIN_RETURN_VALUE            RETURN_MIN( FUNCTION )
  79. #define RETURN_MIN( FUNCTION )      TYPE_PASTE( FUNCTION, _return_min )
  80. /*
  81.  * The minimum values which may be returned by each of the derived functions
  82.  * in the `lround' or the `llround' families.
  83.  */
  84. #define lround_return_min           LONG_MIN
  85. #define lroundf_return_min          LONG_MIN
  86. #define lroundl_return_min          LONG_MIN
  87.  
  88. #define llround_return_min          LLONG_MIN
  89. #define llroundf_return_min         LLONG_MIN
  90. #define llroundl_return_min         LLONG_MIN
  91.  
  92. #define REF_VALUE( VALUE )          REF_TYPE( FUNCTION, VALUE )
  93. #define REF_TYPE( FUNC, VAL )       TYPE_PASTE( FUNC, _ref )( VAL )
  94. /*
  95.  * Macros for expressing constant values of the appropriate data type,
  96.  * for use in each of the derived functions.
  97.  */
  98. #define round_ref( VALUE )          VALUE
  99. #define lround_ref( VALUE )         VALUE
  100. #define llround_ref( VALUE )        VALUE
  101.  
  102. #define roundl_ref( VALUE )         TYPE_PASTE( VALUE, L )
  103. #define lroundl_ref( VALUE )        TYPE_PASTE( VALUE, L )
  104. #define llroundl_ref( VALUE )       TYPE_PASTE( VALUE, L )
  105.  
  106. #define roundf_ref( VALUE )         TYPE_PASTE( VALUE, F )
  107. #define lroundf_ref( VALUE )        TYPE_PASTE( VALUE, F )
  108. #define llroundf_ref( VALUE )       TYPE_PASTE( VALUE, F )
  109.  
  110. static __inline__
  111. INPUT_TYPE __attribute__(( always_inline )) round_internal( INPUT_TYPE x )
  112. #define ROUND_MODES ( FE_TONEAREST | FE_UPWARD | FE_DOWNWARD | FE_TOWARDZERO )
  113. {
  114.   /* Generic helper function, for rounding of the input parameter value to
  115.    * the nearest integer value.
  116.    */
  117.   INPUT_TYPE z;
  118.   unsigned short saved_CW, tmp_required_CW;
  119.  
  120.   /* Rounding method suggested by Danny Smith <dannysmith@users.sf.net>
  121.    *
  122.    * Save the FPU control word state, set rounding mode TONEAREST, round the
  123.    * input value, then restore the original FPU control word state.
  124.    */
  125.   __asm__( "fnstcw %0;" : "=m"( saved_CW ));
  126.   tmp_required_CW = ( saved_CW & ~ROUND_MODES ) | FE_TONEAREST;
  127.   __asm__( "fldcw %0;" :: "m"( tmp_required_CW ));
  128.   __asm__( "frndint;" : "=t"( z ) : "0"( x ));
  129.   __asm__( "fldcw %0;" :: "m"( saved_CW ));
  130.  
  131.   /* We now have a possible rounded value; unfortunately the FPU uses the
  132.    * `round-to-even' rule for exact mid-way cases, where both C99 and POSIX
  133.    * require us to always round away from zero, so we need to adjust those
  134.    * mid-way cases which the FPU rounded in the wrong direction.
  135.    *
  136.    * Correction method suggested by Greg Chicares <gchicares@sbcglobal.net>
  137.    */
  138.   return x < REF_VALUE( 0.0 )
  139.     ? /*
  140.        * For negative input values, an incorrectly rounded value will be
  141.        * exactly 0.5 greater than the original value; when we find such an
  142.        * exact rounding offset, we must subtract an additional 1.0 from the
  143.        * rounded result, otherwise we return the rounded result unchanged.
  144.        */
  145.       z - x == REF_VALUE( 0.5 ) ? z - REF_VALUE( 1.0 ) : z
  146.  
  147.     : /* For positive input values, an incorrectly rounded value will be
  148.        * exactly 0.5 less than the original value; when we find such an exact
  149.        * rounding offset, we must add an additional 1.0 to the rounded result,
  150.        * otherwise we return the rounded result unchanged.
  151.        */
  152.       x - z == REF_VALUE( 0.5 ) ? z + REF_VALUE( 1.0 ) : z;
  153. }
  154.  
  155. #endif /* !defined _ROUND_INTERNAL_H: $RCSfile: round_internal.h,v $: end of file */
  156.