xref: /netbsd-src/external/gpl3/gcc.old/dist/libgcc/config/libbid/bid128_quantize.c (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
1*8feb0f0bSmrg /* Copyright (C) 2007-2020 Free Software Foundation, Inc.
236ac495dSmrg 
336ac495dSmrg This file is part of GCC.
436ac495dSmrg 
536ac495dSmrg GCC is free software; you can redistribute it and/or modify it under
636ac495dSmrg the terms of the GNU General Public License as published by the Free
736ac495dSmrg Software Foundation; either version 3, or (at your option) any later
836ac495dSmrg version.
936ac495dSmrg 
1036ac495dSmrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
1136ac495dSmrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
1236ac495dSmrg FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1336ac495dSmrg for more details.
1436ac495dSmrg 
1536ac495dSmrg Under Section 7 of GPL version 3, you are granted additional
1636ac495dSmrg permissions described in the GCC Runtime Library Exception, version
1736ac495dSmrg 3.1, as published by the Free Software Foundation.
1836ac495dSmrg 
1936ac495dSmrg You should have received a copy of the GNU General Public License and
2036ac495dSmrg a copy of the GCC Runtime Library Exception along with this program;
2136ac495dSmrg see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
2236ac495dSmrg <http://www.gnu.org/licenses/>.  */
2336ac495dSmrg 
2436ac495dSmrg #define BID_128RES
2536ac495dSmrg #include "bid_internal.h"
2636ac495dSmrg 
2736ac495dSmrg BID128_FUNCTION_ARG2 (bid128_quantize, x, y)
2836ac495dSmrg 
2936ac495dSmrg      UINT256 CT;
3036ac495dSmrg      UINT128 CX, CY, T, CX2, CR, Stemp, res, REM_H, C2N;
3136ac495dSmrg      UINT64 sign_x, sign_y, remainder_h, carry, CY64, valid_x;
3236ac495dSmrg      int_float tempx;
3336ac495dSmrg      int exponent_x, exponent_y, digits_x, extra_digits, amount;
3436ac495dSmrg      int expon_diff, total_digits, bin_expon_cx, rmode, status;
3536ac495dSmrg 
3636ac495dSmrg valid_x = unpack_BID128_value (&sign_x, &exponent_x, &CX, x);
3736ac495dSmrg 
3836ac495dSmrg   // unpack arguments, check for NaN or Infinity
3936ac495dSmrg if (!unpack_BID128_value (&sign_y, &exponent_y, &CY, y)) {
4036ac495dSmrg     // y is Inf. or NaN
4136ac495dSmrg #ifdef SET_STATUS_FLAGS
4236ac495dSmrg if ((x.w[1] & SNAN_MASK64) == SNAN_MASK64)	// y is sNaN
4336ac495dSmrg   __set_status_flags (pfpsf, INVALID_EXCEPTION);
4436ac495dSmrg #endif
4536ac495dSmrg 
4636ac495dSmrg     // test if y is NaN
4736ac495dSmrg if ((y.w[1] & 0x7c00000000000000ull) == 0x7c00000000000000ull) {
4836ac495dSmrg #ifdef SET_STATUS_FLAGS
4936ac495dSmrg   if ((y.w[1] & 0x7e00000000000000ull) == 0x7e00000000000000ull) {
5036ac495dSmrg     // set status flags
5136ac495dSmrg     __set_status_flags (pfpsf, INVALID_EXCEPTION);
5236ac495dSmrg   }
5336ac495dSmrg #endif
5436ac495dSmrg   if ((x.w[1] & 0x7c00000000000000ull) != 0x7c00000000000000ull) {
5536ac495dSmrg     res.w[1] = CY.w[1] & QUIET_MASK64;
5636ac495dSmrg     res.w[0] = CY.w[0];
5736ac495dSmrg   } else {
5836ac495dSmrg     res.w[1] = CX.w[1] & QUIET_MASK64;
5936ac495dSmrg     res.w[0] = CX.w[0];
6036ac495dSmrg   }
6136ac495dSmrg   BID_RETURN (res);
6236ac495dSmrg }
6336ac495dSmrg     // y is Infinity?
6436ac495dSmrg if ((y.w[1] & 0x7800000000000000ull) == 0x7800000000000000ull) {
6536ac495dSmrg   // check if x is not Inf.
6636ac495dSmrg   if (((x.w[1] & 0x7c00000000000000ull) < 0x7800000000000000ull)) {
6736ac495dSmrg     // return NaN
6836ac495dSmrg #ifdef SET_STATUS_FLAGS
6936ac495dSmrg     // set status flags
7036ac495dSmrg     __set_status_flags (pfpsf, INVALID_EXCEPTION);
7136ac495dSmrg #endif
7236ac495dSmrg     res.w[1] = 0x7c00000000000000ull;
7336ac495dSmrg     res.w[0] = 0;
7436ac495dSmrg     BID_RETURN (res);
7536ac495dSmrg   } else
7636ac495dSmrg     if (((x.w[1] & 0x7c00000000000000ull) <= 0x7800000000000000ull)) {
7736ac495dSmrg     res.w[1] = CX.w[1] & QUIET_MASK64;
7836ac495dSmrg     res.w[0] = CX.w[0];
7936ac495dSmrg     BID_RETURN (res);
8036ac495dSmrg   }
8136ac495dSmrg }
8236ac495dSmrg 
8336ac495dSmrg }
8436ac495dSmrg 
8536ac495dSmrg if (!valid_x) {
8636ac495dSmrg   // test if x is NaN or Inf
8736ac495dSmrg   if ((x.w[1] & 0x7c00000000000000ull) == 0x7800000000000000ull) {
8836ac495dSmrg #ifdef SET_STATUS_FLAGS
8936ac495dSmrg     // set status flags
9036ac495dSmrg     __set_status_flags (pfpsf, INVALID_EXCEPTION);
9136ac495dSmrg #endif
9236ac495dSmrg     res.w[1] = 0x7c00000000000000ull;
9336ac495dSmrg     res.w[0] = 0;
9436ac495dSmrg     BID_RETURN (res);
9536ac495dSmrg   } else if ((x.w[1] & 0x7c00000000000000ull) == 0x7c00000000000000ull) {
9636ac495dSmrg     if ((x.w[1] & 0x7e00000000000000ull) == 0x7e00000000000000ull) {
9736ac495dSmrg #ifdef SET_STATUS_FLAGS
9836ac495dSmrg       // set status flags
9936ac495dSmrg       __set_status_flags (pfpsf, INVALID_EXCEPTION);
10036ac495dSmrg #endif
10136ac495dSmrg     }
10236ac495dSmrg     res.w[1] = CX.w[1] & QUIET_MASK64;
10336ac495dSmrg     res.w[0] = CX.w[0];
10436ac495dSmrg     BID_RETURN (res);
10536ac495dSmrg   }
10636ac495dSmrg   if (!CX.w[1] && !CX.w[0]) {
10736ac495dSmrg     get_BID128_very_fast (&res, sign_x, exponent_y, CX);
10836ac495dSmrg     BID_RETURN (res);
10936ac495dSmrg   }
11036ac495dSmrg }
11136ac495dSmrg   // get number of decimal digits in coefficient_x
11236ac495dSmrg if (CX.w[1]) {
11336ac495dSmrg   tempx.d = (float) CX.w[1];
11436ac495dSmrg   bin_expon_cx = ((tempx.i >> 23) & 0xff) - 0x7f + 64;
11536ac495dSmrg } else {
11636ac495dSmrg   tempx.d = (float) CX.w[0];
11736ac495dSmrg   bin_expon_cx = ((tempx.i >> 23) & 0xff) - 0x7f;
11836ac495dSmrg }
11936ac495dSmrg 
12036ac495dSmrg digits_x = estimate_decimal_digits[bin_expon_cx];
12136ac495dSmrg if (CX.w[1] > power10_table_128[digits_x].w[1]
12236ac495dSmrg     || (CX.w[1] == power10_table_128[digits_x].w[1]
12336ac495dSmrg 	&& CX.w[0] >= power10_table_128[digits_x].w[0]))
12436ac495dSmrg   digits_x++;
12536ac495dSmrg 
12636ac495dSmrg expon_diff = exponent_x - exponent_y;
12736ac495dSmrg total_digits = digits_x + expon_diff;
12836ac495dSmrg 
12936ac495dSmrg if ((UINT32) total_digits <= 34) {
13036ac495dSmrg   if (expon_diff >= 0) {
13136ac495dSmrg     T = power10_table_128[expon_diff];
13236ac495dSmrg     __mul_128x128_low (CX2, T, CX);
13336ac495dSmrg     get_BID128_very_fast (&res, sign_x, exponent_y, CX2);
13436ac495dSmrg     BID_RETURN (res);
13536ac495dSmrg   }
13636ac495dSmrg #ifndef IEEE_ROUND_NEAREST_TIES_AWAY
13736ac495dSmrg #ifndef IEEE_ROUND_NEAREST
13836ac495dSmrg   rmode = rnd_mode;
13936ac495dSmrg   if (sign_x && (unsigned) (rmode - 1) < 2)
14036ac495dSmrg     rmode = 3 - rmode;
14136ac495dSmrg #else
14236ac495dSmrg   rmode = 0;
14336ac495dSmrg #endif
14436ac495dSmrg #else
14536ac495dSmrg   rmode = 0;
14636ac495dSmrg #endif
14736ac495dSmrg   // must round off -expon_diff digits
14836ac495dSmrg   extra_digits = -expon_diff;
14936ac495dSmrg   __add_128_128 (CX, CX, round_const_table_128[rmode][extra_digits]);
15036ac495dSmrg 
15136ac495dSmrg   // get P*(2^M[extra_digits])/10^extra_digits
15236ac495dSmrg   __mul_128x128_to_256 (CT, CX, reciprocals10_128[extra_digits]);
15336ac495dSmrg 
15436ac495dSmrg   // now get P/10^extra_digits: shift C64 right by M[extra_digits]-128
15536ac495dSmrg   amount = recip_scale[extra_digits];
15636ac495dSmrg   CX2.w[0] = CT.w[2];
15736ac495dSmrg   CX2.w[1] = CT.w[3];
15836ac495dSmrg   if (amount >= 64) {
15936ac495dSmrg     CR.w[1] = 0;
16036ac495dSmrg     CR.w[0] = CX2.w[1] >> (amount - 64);
16136ac495dSmrg   } else {
16236ac495dSmrg     __shr_128 (CR, CX2, amount);
16336ac495dSmrg   }
16436ac495dSmrg 
16536ac495dSmrg #ifndef IEEE_ROUND_NEAREST_TIES_AWAY
16636ac495dSmrg #ifndef IEEE_ROUND_NEAREST
16736ac495dSmrg   if (rnd_mode == 0)
16836ac495dSmrg #endif
16936ac495dSmrg     if (CR.w[0] & 1) {
17036ac495dSmrg       // check whether fractional part of initial_P/10^extra_digits is
17136ac495dSmrg       // exactly .5 this is the same as fractional part of
17236ac495dSmrg       // (initial_P + 0.5*10^extra_digits)/10^extra_digits is exactly zero
17336ac495dSmrg 
17436ac495dSmrg       // get remainder
17536ac495dSmrg       if (amount >= 64) {
17636ac495dSmrg 	remainder_h = CX2.w[0] | (CX2.w[1] << (128 - amount));
17736ac495dSmrg       } else
17836ac495dSmrg 	remainder_h = CX2.w[0] << (64 - amount);
17936ac495dSmrg 
18036ac495dSmrg       // test whether fractional part is 0
18136ac495dSmrg       if (!remainder_h
18236ac495dSmrg 	  && (CT.w[1] < reciprocals10_128[extra_digits].w[1]
18336ac495dSmrg 	      || (CT.w[1] == reciprocals10_128[extra_digits].w[1]
18436ac495dSmrg 		  && CT.w[0] < reciprocals10_128[extra_digits].w[0]))) {
18536ac495dSmrg 	CR.w[0]--;
18636ac495dSmrg       }
18736ac495dSmrg     }
18836ac495dSmrg #endif
18936ac495dSmrg 
19036ac495dSmrg #ifdef SET_STATUS_FLAGS
19136ac495dSmrg   status = INEXACT_EXCEPTION;
19236ac495dSmrg 
19336ac495dSmrg   // get remainder
19436ac495dSmrg   if (amount >= 64) {
19536ac495dSmrg     REM_H.w[1] = (CX2.w[1] << (128 - amount));
19636ac495dSmrg     REM_H.w[0] = CX2.w[0];
19736ac495dSmrg   } else {
19836ac495dSmrg     REM_H.w[1] = CX2.w[0] << (64 - amount);
19936ac495dSmrg     REM_H.w[0] = 0;
20036ac495dSmrg   }
20136ac495dSmrg 
20236ac495dSmrg   switch (rmode) {
20336ac495dSmrg   case ROUNDING_TO_NEAREST:
20436ac495dSmrg   case ROUNDING_TIES_AWAY:
20536ac495dSmrg     // test whether fractional part is 0
20636ac495dSmrg     if (REM_H.w[1] == 0x8000000000000000ull && !REM_H.w[0]
20736ac495dSmrg 	&& (CT.w[1] < reciprocals10_128[extra_digits].w[1]
20836ac495dSmrg 	    || (CT.w[1] == reciprocals10_128[extra_digits].w[1]
20936ac495dSmrg 		&& CT.w[0] < reciprocals10_128[extra_digits].w[0])))
21036ac495dSmrg       status = EXACT_STATUS;
21136ac495dSmrg     break;
21236ac495dSmrg   case ROUNDING_DOWN:
21336ac495dSmrg   case ROUNDING_TO_ZERO:
21436ac495dSmrg     if (!(REM_H.w[1] | REM_H.w[0])
21536ac495dSmrg 	&& (CT.w[1] < reciprocals10_128[extra_digits].w[1]
21636ac495dSmrg 	    || (CT.w[1] == reciprocals10_128[extra_digits].w[1]
21736ac495dSmrg 		&& CT.w[0] < reciprocals10_128[extra_digits].w[0])))
21836ac495dSmrg       status = EXACT_STATUS;
21936ac495dSmrg     break;
22036ac495dSmrg   default:
22136ac495dSmrg     // round up
22236ac495dSmrg     __add_carry_out (Stemp.w[0], CY64, CT.w[0],
22336ac495dSmrg 		     reciprocals10_128[extra_digits].w[0]);
22436ac495dSmrg     __add_carry_in_out (Stemp.w[1], carry, CT.w[1],
22536ac495dSmrg 			reciprocals10_128[extra_digits].w[1], CY64);
22636ac495dSmrg     if (amount < 64) {
22736ac495dSmrg       C2N.w[1] = 0;
22836ac495dSmrg       C2N.w[0] = ((UINT64) 1) << amount;
22936ac495dSmrg       REM_H.w[0] = REM_H.w[1] >> (64 - amount);
23036ac495dSmrg       REM_H.w[1] = 0;
23136ac495dSmrg     } else {
23236ac495dSmrg       C2N.w[1] = ((UINT64) 1) << (amount - 64);
23336ac495dSmrg       C2N.w[0] = 0;
23436ac495dSmrg       REM_H.w[1] >>= (128 - amount);
23536ac495dSmrg     }
23636ac495dSmrg     REM_H.w[0] += carry;
23736ac495dSmrg     if (REM_H.w[0] < carry)
23836ac495dSmrg       REM_H.w[1]++;
23936ac495dSmrg     if (__unsigned_compare_ge_128 (REM_H, C2N))
24036ac495dSmrg       status = EXACT_STATUS;
24136ac495dSmrg   }
24236ac495dSmrg 
24336ac495dSmrg   __set_status_flags (pfpsf, status);
24436ac495dSmrg 
24536ac495dSmrg #endif
24636ac495dSmrg   get_BID128_very_fast (&res, sign_x, exponent_y, CR);
24736ac495dSmrg   BID_RETURN (res);
24836ac495dSmrg }
24936ac495dSmrg if (total_digits < 0) {
25036ac495dSmrg   CR.w[1] = CR.w[0] = 0;
25136ac495dSmrg #ifndef IEEE_ROUND_NEAREST_TIES_AWAY
25236ac495dSmrg #ifndef IEEE_ROUND_NEAREST
25336ac495dSmrg   rmode = rnd_mode;
25436ac495dSmrg   if (sign_x && (unsigned) (rmode - 1) < 2)
25536ac495dSmrg     rmode = 3 - rmode;
25636ac495dSmrg   if (rmode == ROUNDING_UP)
25736ac495dSmrg     CR.w[0] = 1;
25836ac495dSmrg #endif
25936ac495dSmrg #endif
26036ac495dSmrg #ifdef SET_STATUS_FLAGS
26136ac495dSmrg   __set_status_flags (pfpsf, INEXACT_EXCEPTION);
26236ac495dSmrg #endif
26336ac495dSmrg   get_BID128_very_fast (&res, sign_x, exponent_y, CR);
26436ac495dSmrg   BID_RETURN (res);
26536ac495dSmrg }
26636ac495dSmrg   // else  more than 34 digits in coefficient
26736ac495dSmrg #ifdef SET_STATUS_FLAGS
26836ac495dSmrg __set_status_flags (pfpsf, INVALID_EXCEPTION);
26936ac495dSmrg #endif
27036ac495dSmrg res.w[1] = 0x7c00000000000000ull;
27136ac495dSmrg res.w[0] = 0;
27236ac495dSmrg BID_RETURN (res);
27336ac495dSmrg 
27436ac495dSmrg }
275