/* Copyright (C) 2007, 2009 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Under Section 7 of GPL version 3, you are granted additional permissions described in the GCC Runtime Library Exception, version 3.1, as published by the Free Software Foundation. You should have received a copy of the GNU General Public License and a copy of the GCC Runtime Library Exception along with this program; see the files COPYING3 and COPYING.RUNTIME respectively. If not, see . */ #define BID_128RES #include "bid_internal.h" /* * Takes a BID64 as input and converts it to a BID128 and returns it. */ TYPE0_FUNCTION_ARGTYPE1_NORND (UINT128, bid64_to_bid128, UINT64, x) UINT128 new_coeff, res; UINT64 sign_x; int exponent_x; UINT64 coefficient_x; if (!unpack_BID64 (&sign_x, &exponent_x, &coefficient_x, x)) { if (((x) << 1) >= 0xf000000000000000ull) { #ifdef SET_STATUS_FLAGS if (((x) & SNAN_MASK64) == SNAN_MASK64) // sNaN __set_status_flags (pfpsf, INVALID_EXCEPTION); #endif res.w[0] = (coefficient_x & 0x0003ffffffffffffull); __mul_64x64_to_128 (res, res.w[0], power10_table_128[18].w[0]); res.w[1] |= ((coefficient_x) & 0xfc00000000000000ull); BID_RETURN (res); } } new_coeff.w[0] = coefficient_x; new_coeff.w[1] = 0; get_BID128_very_fast (&res, sign_x, exponent_x + DECIMAL_EXPONENT_BIAS_128 - DECIMAL_EXPONENT_BIAS, new_coeff); BID_RETURN (res); } // convert_bid64_to_bid128 /* * Takes a BID128 as input and converts it to a BID64 and returns it. */ #if DECIMAL_CALL_BY_REFERENCE void bid128_to_bid64 (UINT64 * pres, UINT128 * px _RND_MODE_PARAM _EXC_FLAGS_PARAM _EXC_MASKS_PARAM _EXC_INFO_PARAM) { UINT128 x = *px; #else UINT64 bid128_to_bid64 (UINT128 x _RND_MODE_PARAM _EXC_FLAGS_PARAM _EXC_MASKS_PARAM _EXC_INFO_PARAM) { #endif UINT128 CX, T128, TP128, Qh, Ql, Qh1, Stemp, Tmp, Tmp1, CX1; UINT64 sign_x, carry, cy, res; SINT64 D; int_float f64, fx; int exponent_x, extra_digits, amount, bin_expon_cx; unsigned rmode, status, uf_check = 0; #if DECIMAL_CALL_BY_REFERENCE #if !DECIMAL_GLOBAL_ROUNDING _IDEC_round rnd_mode = *prnd_mode; #endif #endif BID_SWAP128 (x); // unpack arguments, check for NaN or Infinity or 0 if (!unpack_BID128_value (&sign_x, &exponent_x, &CX, x)) { if ((x.w[1] << 1) >= 0xf000000000000000ull) { Tmp.w[1] = (CX.w[1] & 0x00003fffffffffffull); Tmp.w[0] = CX.w[0]; TP128 = reciprocals10_128[18]; __mul_128x128_full (Qh, Ql, Tmp, TP128); amount = recip_scale[18]; __shr_128 (Tmp, Qh, amount); res = (CX.w[1] & 0xfc00000000000000ull) | Tmp.w[0]; #ifdef SET_STATUS_FLAGS if ((x.w[1] & SNAN_MASK64) == SNAN_MASK64) // sNaN __set_status_flags (pfpsf, INVALID_EXCEPTION); #endif BID_RETURN_VAL (res); } exponent_x = exponent_x - DECIMAL_EXPONENT_BIAS_128 + DECIMAL_EXPONENT_BIAS; if (exponent_x < 0) { res = sign_x; BID_RETURN_VAL (res); } if (exponent_x > DECIMAL_MAX_EXPON_64) exponent_x = DECIMAL_MAX_EXPON_64; res = sign_x | (((UINT64) exponent_x) << 53); BID_RETURN_VAL (res); } if (CX.w[1] || (CX.w[0] >= 10000000000000000ull)) { // find number of digits in coefficient // 2^64 f64.i = 0x5f800000; // fx ~ CX fx.d = (float) CX.w[1] * f64.d + (float) CX.w[0]; bin_expon_cx = ((fx.i >> 23) & 0xff) - 0x7f; extra_digits = estimate_decimal_digits[bin_expon_cx] - 16; // scale = 38-estimate_decimal_digits[bin_expon_cx]; D = CX.w[1] - power10_index_binexp_128[bin_expon_cx].w[1]; if (D > 0 || (!D && CX.w[0] >= power10_index_binexp_128[bin_expon_cx].w[0])) extra_digits++; exponent_x += extra_digits; #ifndef IEEE_ROUND_NEAREST_TIES_AWAY #ifndef IEEE_ROUND_NEAREST rmode = rnd_mode; if (sign_x && (unsigned) (rmode - 1) < 2) rmode = 3 - rmode; #else rmode = 0; #endif #else rmode = 0; #endif if (exponent_x < DECIMAL_EXPONENT_BIAS_128 - DECIMAL_EXPONENT_BIAS) { uf_check = 1; if (-extra_digits + exponent_x - DECIMAL_EXPONENT_BIAS_128 + DECIMAL_EXPONENT_BIAS + 35 >= 0) { if (exponent_x == DECIMAL_EXPONENT_BIAS_128 - DECIMAL_EXPONENT_BIAS - 1) { T128 = round_const_table_128[rmode][extra_digits]; __add_carry_out (CX1.w[0], carry, T128.w[0], CX.w[0]); CX1.w[1] = CX.w[1] + T128.w[1] + carry; if (__unsigned_compare_ge_128 (CX1, power10_table_128[extra_digits + 16])) uf_check = 0; } extra_digits = extra_digits + DECIMAL_EXPONENT_BIAS_128 - DECIMAL_EXPONENT_BIAS - exponent_x; exponent_x = DECIMAL_EXPONENT_BIAS_128 - DECIMAL_EXPONENT_BIAS; //uf_check = 2; } else rmode = ROUNDING_TO_ZERO; } T128 = round_const_table_128[rmode][extra_digits]; __add_carry_out (CX.w[0], carry, T128.w[0], CX.w[0]); CX.w[1] = CX.w[1] + T128.w[1] + carry; TP128 = reciprocals10_128[extra_digits]; __mul_128x128_full (Qh, Ql, CX, TP128); amount = recip_scale[extra_digits]; if (amount >= 64) { CX.w[0] = Qh.w[1] >> (amount - 64); CX.w[1] = 0; } else { __shr_128 (CX, Qh, amount); } #ifndef IEEE_ROUND_NEAREST_TIES_AWAY #ifndef IEEE_ROUND_NEAREST if (!(rmode)) #endif if (CX.w[0] & 1) { // check whether fractional part of initial_P/10^ed1 is exactly .5 // get remainder __shl_128_long (Qh1, Qh, (128 - amount)); if (!Qh1.w[1] && !Qh1.w[0] && (Ql.w[1] < reciprocals10_128[extra_digits].w[1] || (Ql.w[1] == reciprocals10_128[extra_digits].w[1] && Ql.w[0] < reciprocals10_128[extra_digits].w[0]))) { CX.w[0]--; } } #endif { status = INEXACT_EXCEPTION; // get remainder __shl_128_long (Qh1, Qh, (128 - amount)); switch (rmode) { case ROUNDING_TO_NEAREST: case ROUNDING_TIES_AWAY: // test whether fractional part is 0 if (Qh1.w[1] == 0x8000000000000000ull && (!Qh1.w[0]) && (Ql.w[1] < reciprocals10_128[extra_digits].w[1] || (Ql.w[1] == reciprocals10_128[extra_digits].w[1] && Ql.w[0] < reciprocals10_128[extra_digits].w[0]))) status = EXACT_STATUS; break; case ROUNDING_DOWN: case ROUNDING_TO_ZERO: if ((!Qh1.w[1]) && (!Qh1.w[0]) && (Ql.w[1] < reciprocals10_128[extra_digits].w[1] || (Ql.w[1] == reciprocals10_128[extra_digits].w[1] && Ql.w[0] < reciprocals10_128[extra_digits].w[0]))) status = EXACT_STATUS; break; default: // round up __add_carry_out (Stemp.w[0], cy, Ql.w[0], reciprocals10_128[extra_digits].w[0]); __add_carry_in_out (Stemp.w[1], carry, Ql.w[1], reciprocals10_128[extra_digits].w[1], cy); __shr_128_long (Qh, Qh1, (128 - amount)); Tmp.w[0] = 1; Tmp.w[1] = 0; __shl_128_long (Tmp1, Tmp, amount); Qh.w[0] += carry; if (Qh.w[0] < carry) Qh.w[1]++; if (__unsigned_compare_ge_128 (Qh, Tmp1)) status = EXACT_STATUS; } if (status != EXACT_STATUS) { if (uf_check) status |= UNDERFLOW_EXCEPTION; #ifdef SET_STATUS_FLAGS __set_status_flags (pfpsf, status); #endif } } } res = get_BID64 (sign_x, exponent_x - DECIMAL_EXPONENT_BIAS_128 + DECIMAL_EXPONENT_BIAS, CX.w[0], rnd_mode, pfpsf); BID_RETURN_VAL (res); }