(root)/
glibc-2.38/
sysdeps/
powerpc/
fpu/
round_to_integer.h
       1  /* Round to integer generic implementation.
       2     Copyright (C) 2019-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Library General Public License as
       7     published by the Free Software Foundation; either version 2 of the
       8     License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Library General Public License for more details.
      14  
      15     You should have received a copy of the GNU Library General Public
      16     License along with the GNU C Library; see the file COPYING.LIB.  If
      17     not, see <https://www.gnu.org/licenses/>.  */
      18  
      19  #ifndef _ROUND_TO_INTEGER_H
      20  #define _ROUND_TO_INTEGER_H
      21  
      22  #include <fenv_private.h>
      23  
      24  enum round_mode
      25  {
      26    CEIL,
      27    FLOOR,
      28    ROUND,
      29    TRUNC,
      30    NEARBYINT,
      31    RINT
      32  };
      33  
      34  static inline fenv_t
      35  set_fenv_mode (enum round_mode mode)
      36  {
      37    fenv_t fe = 0;
      38    if (mode != RINT)
      39      /* Save current FPU rounding mode and inexact state.  */
      40      fe = fegetenv_register ();
      41  
      42    switch (mode)
      43    {
      44    case CEIL:
      45      __fesetround_inline_nocheck (FE_UPWARD);
      46      break;
      47    case FLOOR:
      48      __fesetround_inline_nocheck (FE_DOWNWARD);
      49      break;
      50    case TRUNC:
      51    case ROUND:
      52      __fesetround_inline_nocheck (FE_TOWARDZERO);
      53      break;
      54    case NEARBYINT:
      55      /*  Disable FE_INEXACT exception  */
      56      reset_fpscr_bit (FPSCR_XE);
      57      break;
      58    case RINT:
      59      break;
      60    }
      61    return fe;
      62  }
      63  
      64  static inline void
      65  reset_fenv_mode (fenv_t fe, enum round_mode mode)
      66  {
      67    switch (mode)
      68    {
      69    default:
      70      __builtin_mtfsf (0xff, fe);
      71      break;
      72    case RINT:
      73      break;
      74    }
      75  }
      76  
      77  static inline float
      78  round_to_integer_float (enum round_mode mode, float x)
      79  {
      80    /* Ensure sNaN input is converted to qNaN.  */
      81    if (__glibc_unlikely (isnan (x)))
      82      return x + x;
      83  
      84    if (fabs (x) > 0x1p+23)
      85      return x;
      86  
      87    float r = x;
      88  
      89    fenv_t fe = set_fenv_mode (mode);
      90    if (x > 0.0)
      91      {
      92        /* IEEE 1003.1 round function.  IEEE specifies "round to the nearest
      93  	 integer value, rounding halfway cases away from zero, regardless of
      94  	 the current rounding mode."  However PowerPC Architecture defines
      95  	 "Round to Nearest" as "Choose the best approximation. In case of a
      96  	 tie, choose the one that is even (least significant bit o).".
      97  	 So we can't use the PowerPC "Round to Nearest" mode. Instead we set
      98  	 "Round toward Zero" mode and round by adding +-0.5 before rounding
      99  	 to the integer value.  */
     100        if (mode == ROUND)
     101  	r += 0.5f;
     102        r += 0x1p+23;
     103        r -= 0x1p+23;
     104        r = fabs (r);
     105      }
     106    else if (x < 0.0)
     107      {
     108        if (mode == ROUND)
     109  	r -= 0.5f;
     110        r -= 0x1p+23;
     111        r += 0x1p+23;
     112        r = -fabs (r);
     113      }
     114    reset_fenv_mode (fe, mode);
     115  
     116    return r;
     117  }
     118  
     119  static inline double
     120  round_to_integer_double (enum round_mode mode, double x)
     121  {
     122    /* Ensure sNaN input is converted to qNaN.  */
     123    if (__glibc_unlikely (isnan (x)))
     124      return x + x;
     125  
     126    if (fabs (x) > 0x1p+52)
     127      return x;
     128  
     129    double r = x;
     130  
     131    /* Save current FPU rounding mode and inexact state.  */
     132    fenv_t fe = set_fenv_mode (mode);
     133    if (x > 0.0)
     134      {
     135        if (mode == ROUND)
     136  	r += 0.5;
     137        r += 0x1p+52;
     138        r -= 0x1p+52;
     139        r = fabs (r);
     140      }
     141    else if (x < 0.0)
     142      {
     143        if (mode == ROUND)
     144  	r -= 0.5;
     145        r -= 0x1p+52;
     146        r += 0x1p+52;
     147        r = -fabs (r);
     148      }
     149    reset_fenv_mode (fe, mode);
     150  
     151    return r;
     152  }
     153  
     154  #endif