round.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /* Round toward nearest, breaking ties away from zero.
  2. Copyright (C) 2007, 2010-2017 Free Software Foundation, Inc.
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU Lesser General Public License as published by
  5. the Free Software Foundation; either version 2, or (at your option)
  6. any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License along
  12. with this program; if not, see <http://www.gnu.org/licenses/>. */
  13. /* Written by Ben Pfaff <blp@gnu.org>, 2007.
  14. Based heavily on code by Bruno Haible. */
  15. #if ! defined USE_LONG_DOUBLE
  16. # include <config.h>
  17. #endif
  18. /* Specification. */
  19. #include <math.h>
  20. #include <float.h>
  21. #undef MIN
  22. #ifdef USE_LONG_DOUBLE
  23. # define ROUND roundl
  24. # define FLOOR floorl
  25. # define CEIL ceill
  26. # define DOUBLE long double
  27. # define MANT_DIG LDBL_MANT_DIG
  28. # define MIN LDBL_MIN
  29. # define L_(literal) literal##L
  30. # define HAVE_FLOOR_AND_CEIL HAVE_FLOORL_AND_CEILL
  31. #elif ! defined USE_FLOAT
  32. # define ROUND round
  33. # define FLOOR floor
  34. # define CEIL ceil
  35. # define DOUBLE double
  36. # define MANT_DIG DBL_MANT_DIG
  37. # define MIN DBL_MIN
  38. # define L_(literal) literal
  39. # define HAVE_FLOOR_AND_CEIL 1
  40. #else /* defined USE_FLOAT */
  41. # define ROUND roundf
  42. # define FLOOR floorf
  43. # define CEIL ceilf
  44. # define DOUBLE float
  45. # define MANT_DIG FLT_MANT_DIG
  46. # define MIN FLT_MIN
  47. # define L_(literal) literal##f
  48. # define HAVE_FLOOR_AND_CEIL HAVE_FLOORF_AND_CEILF
  49. #endif
  50. /* -0.0. See minus-zero.h. */
  51. #if defined __hpux || defined __sgi || defined __ICC
  52. # define MINUS_ZERO (-MIN * MIN)
  53. #else
  54. # define MINUS_ZERO L_(-0.0)
  55. #endif
  56. /* MSVC with option -fp:strict refuses to compile constant initializers that
  57. contain floating-point operations. Pacify this compiler. */
  58. #ifdef _MSC_VER
  59. # pragma fenv_access (off)
  60. #endif
  61. /* If we're being included from test-round2[f].c, it already defined names for
  62. our round implementations. Otherwise, pick the preferred implementation for
  63. this machine. */
  64. #if !defined FLOOR_BASED_ROUND && !defined FLOOR_FREE_ROUND
  65. # if HAVE_FLOOR_AND_CEIL
  66. # define FLOOR_BASED_ROUND ROUND
  67. # else
  68. # define FLOOR_FREE_ROUND ROUND
  69. # endif
  70. #endif
  71. #ifdef FLOOR_BASED_ROUND
  72. /* An implementation of the C99 round function based on floor and ceil. We use
  73. this when floor and ceil are available, on the assumption that they are
  74. faster than the open-coded versions below. */
  75. DOUBLE
  76. FLOOR_BASED_ROUND (DOUBLE x)
  77. {
  78. if (x >= L_(0.0))
  79. {
  80. DOUBLE y = FLOOR (x);
  81. if (x - y >= L_(0.5))
  82. y += L_(1.0);
  83. return y;
  84. }
  85. else
  86. {
  87. DOUBLE y = CEIL (x);
  88. if (y - x >= L_(0.5))
  89. y -= L_(1.0);
  90. return y;
  91. }
  92. }
  93. #endif /* FLOOR_BASED_ROUND */
  94. #ifdef FLOOR_FREE_ROUND
  95. /* An implementation of the C99 round function without floor or ceil.
  96. We use this when floor or ceil is missing. */
  97. DOUBLE
  98. FLOOR_FREE_ROUND (DOUBLE x)
  99. {
  100. /* 2^(MANT_DIG-1). */
  101. static const DOUBLE TWO_MANT_DIG =
  102. /* Assume MANT_DIG <= 5 * 31.
  103. Use the identity
  104. n = floor(n/5) + floor((n+1)/5) + ... + floor((n+4)/5). */
  105. (DOUBLE) (1U << ((MANT_DIG - 1) / 5))
  106. * (DOUBLE) (1U << ((MANT_DIG - 1 + 1) / 5))
  107. * (DOUBLE) (1U << ((MANT_DIG - 1 + 2) / 5))
  108. * (DOUBLE) (1U << ((MANT_DIG - 1 + 3) / 5))
  109. * (DOUBLE) (1U << ((MANT_DIG - 1 + 4) / 5));
  110. /* The use of 'volatile' guarantees that excess precision bits are dropped at
  111. each addition step and before the following comparison at the caller's
  112. site. It is necessary on x86 systems where double-floats are not IEEE
  113. compliant by default, to avoid that the results become platform and
  114. compiler option dependent. 'volatile' is a portable alternative to gcc's
  115. -ffloat-store option. */
  116. volatile DOUBLE y = x;
  117. volatile DOUBLE z = y;
  118. if (z > L_(0.0))
  119. {
  120. /* Avoid rounding error for x = 0.5 - 2^(-MANT_DIG-1). */
  121. if (z < L_(0.5))
  122. z = L_(0.0);
  123. /* Avoid rounding errors for values near 2^k, where k >= MANT_DIG-1. */
  124. else if (z < TWO_MANT_DIG)
  125. {
  126. /* Add 0.5 to the absolute value. */
  127. y = z += L_(0.5);
  128. /* Round to the next integer (nearest or up or down, doesn't
  129. matter). */
  130. z += TWO_MANT_DIG;
  131. z -= TWO_MANT_DIG;
  132. /* Enforce rounding down. */
  133. if (z > y)
  134. z -= L_(1.0);
  135. }
  136. }
  137. else if (z < L_(0.0))
  138. {
  139. /* Avoid rounding error for x = -(0.5 - 2^(-MANT_DIG-1)). */
  140. if (z > - L_(0.5))
  141. z = MINUS_ZERO;
  142. /* Avoid rounding errors for values near -2^k, where k >= MANT_DIG-1. */
  143. else if (z > -TWO_MANT_DIG)
  144. {
  145. /* Add 0.5 to the absolute value. */
  146. y = z -= L_(0.5);
  147. /* Round to the next integer (nearest or up or down, doesn't
  148. matter). */
  149. z -= TWO_MANT_DIG;
  150. z += TWO_MANT_DIG;
  151. /* Enforce rounding up. */
  152. if (z < y)
  153. z += L_(1.0);
  154. }
  155. }
  156. return z;
  157. }
  158. #endif /* FLOOR_FREE_ROUND */