/* Copyright (C) 2007 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 2, or (at your option) any later version. In addition to the permissions in the GNU General Public License, the Free Software Foundation gives you unlimited permission to link the compiled version of this file into combinations with other programs, and to distribute those combinations without any restriction coming from the use of this file. (The General Public License restrictions do apply in other respects; for example, they cover modification of the file, and distribution when not linked into a combine executable.) 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. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "bid_internal.h" /***************************************************************************** * BID64_round_integral_exact ****************************************************************************/ #if DECIMAL_CALL_BY_REFERENCE void bid64_from_int32 (UINT64 * pres, int *px _EXC_MASKS_PARAM _EXC_INFO_PARAM) { int x = *px; #else UINT64 bid64_from_int32 (int x _EXC_MASKS_PARAM _EXC_INFO_PARAM) { #endif UINT64 res; // if integer is negative, put the absolute value // in the lowest 32bits of the result if ((x & SIGNMASK32) == SIGNMASK32) { // negative int32 x = ~x + 1; // 2's complement of x res = (unsigned int) x | 0xb1c0000000000000ull; // (exp << 53)) = biased exp. is 0 } else { // positive int32 res = x | 0x31c0000000000000ull; // (exp << 53)) = biased exp. is 0 } BID_RETURN (res); } #if DECIMAL_CALL_BY_REFERENCE void bid64_from_uint32 (UINT64 * pres, unsigned int *px _EXC_MASKS_PARAM _EXC_INFO_PARAM) { unsigned int x = *px; #else UINT64 bid64_from_uint32 (unsigned int x _EXC_MASKS_PARAM _EXC_INFO_PARAM) { #endif UINT64 res; res = x | 0x31c0000000000000ull; // (exp << 53)) = biased exp. is 0 BID_RETURN (res); } #if DECIMAL_CALL_BY_REFERENCE void bid64_from_int64 (UINT64 * pres, SINT64 * px _RND_MODE_PARAM _EXC_FLAGS_PARAM _EXC_MASKS_PARAM _EXC_INFO_PARAM) { SINT64 x = *px; #if !DECIMAL_GLOBAL_ROUNDING unsigned int rnd_mode = *prnd_mode; #endif #else UINT64 bid64_from_int64 (SINT64 x _RND_MODE_PARAM _EXC_FLAGS_PARAM _EXC_MASKS_PARAM _EXC_INFO_PARAM) { #endif UINT64 res; UINT64 x_sign, C; unsigned int q, ind; int incr_exp = 0; int is_midpoint_lt_even = 0, is_midpoint_gt_even = 0; int is_inexact_lt_midpoint = 0, is_inexact_gt_midpoint = 0; x_sign = x & 0x8000000000000000ull; // if the integer is negative, use the absolute value if (x_sign) C = ~((UINT64) x) + 1; else C = x; if (C <= BID64_SIG_MAX) { // |C| <= 10^16-1 and the result is exact if (C < 0x0020000000000000ull) { // C < 2^53 res = x_sign | 0x31c0000000000000ull | C; } else { // C >= 2^53 res = x_sign | 0x6c70000000000000ull | (C & 0x0007ffffffffffffull); } } else { // |C| >= 10^16 and the result may be inexact // the smallest |C| is 10^16 which has 17 decimal digits // the largest |C| is 0x8000000000000000 = 9223372036854775808 w/ 19 digits if (C < 0x16345785d8a0000ull) { // x < 10^17 q = 17; ind = 1; // number of digits to remove for q = 17 } else if (C < 0xde0b6b3a7640000ull) { // C < 10^18 q = 18; ind = 2; // number of digits to remove for q = 18 } else { // C < 10^19 q = 19; ind = 3; // number of digits to remove for q = 19 } // overflow and underflow are not possible // Note: performace can be improved by inlining this call round64_2_18 ( // will work for 19 digits too if C fits in 64 bits q, ind, C, &res, &incr_exp, &is_midpoint_lt_even, &is_midpoint_gt_even, &is_inexact_lt_midpoint, &is_inexact_gt_midpoint); if (incr_exp) ind++; // set the inexact flag if (is_inexact_lt_midpoint || is_inexact_gt_midpoint || is_midpoint_lt_even || is_midpoint_gt_even) *pfpsf |= INEXACT_EXCEPTION; // general correction from RN to RA, RM, RP, RZ; result uses ind for exp if (rnd_mode != ROUNDING_TO_NEAREST) { if ((!x_sign && ((rnd_mode == ROUNDING_UP && is_inexact_lt_midpoint) || ((rnd_mode == ROUNDING_TIES_AWAY || rnd_mode == ROUNDING_UP) && is_midpoint_gt_even))) || (x_sign && ((rnd_mode == ROUNDING_DOWN && is_inexact_lt_midpoint) || ((rnd_mode == ROUNDING_TIES_AWAY || rnd_mode == ROUNDING_DOWN) && is_midpoint_gt_even)))) { res = res + 1; if (res == 0x002386f26fc10000ull) { // res = 10^16 => rounding overflow res = 0x00038d7ea4c68000ull; // 10^15 ind = ind + 1; } } else if ((is_midpoint_lt_even || is_inexact_gt_midpoint) && ((x_sign && (rnd_mode == ROUNDING_UP || rnd_mode == ROUNDING_TO_ZERO)) || (!x_sign && (rnd_mode == ROUNDING_DOWN || rnd_mode == ROUNDING_TO_ZERO)))) { res = res - 1; // check if we crossed into the lower decade if (res == 0x00038d7ea4c67fffull) { // 10^15 - 1 res = 0x002386f26fc0ffffull; // 10^16 - 1 ind = ind - 1; } } else { ; // exact, the result is already correct } } if (res < 0x0020000000000000ull) { // res < 2^53 res = x_sign | (((UINT64) ind + 398) << 53) | res; } else { // res >= 2^53 res = x_sign | 0x6000000000000000ull | (((UINT64) ind + 398) << 51) | (res & 0x0007ffffffffffffull); } } BID_RETURN (res); } #if DECIMAL_CALL_BY_REFERENCE void bid64_from_uint64 (UINT64 * pres, UINT64 * px _RND_MODE_PARAM _EXC_FLAGS_PARAM _EXC_MASKS_PARAM _EXC_INFO_PARAM) { UINT64 x = *px; #if !DECIMAL_GLOBAL_ROUNDING unsigned int rnd_mode = *prnd_mode; #endif #else UINT64 bid64_from_uint64 (UINT64 x _RND_MODE_PARAM _EXC_FLAGS_PARAM _EXC_MASKS_PARAM _EXC_INFO_PARAM) { #endif UINT64 res; UINT128 x128, res128; unsigned int q, ind; int incr_exp = 0; int is_midpoint_lt_even = 0, is_midpoint_gt_even = 0; int is_inexact_lt_midpoint = 0, is_inexact_gt_midpoint = 0; if (x <= BID64_SIG_MAX) { // x <= 10^16-1 and the result is exact if (x < 0x0020000000000000ull) { // x < 2^53 res = 0x31c0000000000000ull | x; } else { // x >= 2^53 res = 0x6c70000000000000ull | (x & 0x0007ffffffffffffull); } } else { // x >= 10^16 and the result may be inexact // the smallest x is 10^16 which has 17 decimal digits // the largest x is 0xffffffffffffffff = 18446744073709551615 w/ 20 digits if (x < 0x16345785d8a0000ull) { // x < 10^17 q = 17; ind = 1; // number of digits to remove for q = 17 } else if (x < 0xde0b6b3a7640000ull) { // x < 10^18 q = 18; ind = 2; // number of digits to remove for q = 18 } else if (x < 0x8ac7230489e80000ull) { // x < 10^19 q = 19; ind = 3; // number of digits to remove for q = 19 } else { // x < 10^20 q = 20; ind = 4; // number of digits to remove for q = 20 } // overflow and underflow are not possible // Note: performace can be improved by inlining this call if (q <= 19) { round64_2_18 ( // will work for 20 digits too if x fits in 64 bits q, ind, x, &res, &incr_exp, &is_midpoint_lt_even, &is_midpoint_gt_even, &is_inexact_lt_midpoint, &is_inexact_gt_midpoint); } else { // q = 20 x128.w[1] = 0x0; x128.w[0] = x; round128_19_38 (q, ind, x128, &res128, &incr_exp, &is_midpoint_lt_even, &is_midpoint_gt_even, &is_inexact_lt_midpoint, &is_inexact_gt_midpoint); res = res128.w[0]; // res.w[1] is 0 } if (incr_exp) ind++; // set the inexact flag if (is_inexact_lt_midpoint || is_inexact_gt_midpoint || is_midpoint_lt_even || is_midpoint_gt_even) *pfpsf |= INEXACT_EXCEPTION; // general correction from RN to RA, RM, RP, RZ; result uses ind for exp if (rnd_mode != ROUNDING_TO_NEAREST) { if ((rnd_mode == ROUNDING_UP && is_inexact_lt_midpoint) || ((rnd_mode == ROUNDING_TIES_AWAY || rnd_mode == ROUNDING_UP) && is_midpoint_gt_even)) { res = res + 1; if (res == 0x002386f26fc10000ull) { // res = 10^16 => rounding overflow res = 0x00038d7ea4c68000ull; // 10^15 ind = ind + 1; } } else if ((is_midpoint_lt_even || is_inexact_gt_midpoint) && (rnd_mode == ROUNDING_DOWN || rnd_mode == ROUNDING_TO_ZERO)) { res = res - 1; // check if we crossed into the lower decade if (res == 0x00038d7ea4c67fffull) { // 10^15 - 1 res = 0x002386f26fc0ffffull; // 10^16 - 1 ind = ind - 1; } } else { ; // exact, the result is already correct } } if (res < 0x0020000000000000ull) { // res < 2^53 res = (((UINT64) ind + 398) << 53) | res; } else { // res >= 2^53 res = 0x6000000000000000ull | (((UINT64) ind + 398) << 51) | (res & 0x0007ffffffffffffull); } } BID_RETURN (res); } #if DECIMAL_CALL_BY_REFERENCE void bid128_from_int32 (UINT128 * pres, int *px _EXC_MASKS_PARAM _EXC_INFO_PARAM) { int x = *px; #else UINT128 bid128_from_int32 (int x _EXC_MASKS_PARAM _EXC_INFO_PARAM) { #endif UINT128 res; // if integer is negative, use the absolute value if ((x & SIGNMASK32) == SIGNMASK32) { res.w[HIGH_128W] = 0xb040000000000000ull; res.w[LOW_128W] = ~((unsigned int) x) + 1; // 2's complement of x } else { res.w[HIGH_128W] = 0x3040000000000000ull; res.w[LOW_128W] = (unsigned int) x; } BID_RETURN (res); } #if DECIMAL_CALL_BY_REFERENCE void bid128_from_uint32 (UINT128 * pres, unsigned int *px _EXC_MASKS_PARAM _EXC_INFO_PARAM) { unsigned int x = *px; #else UINT128 bid128_from_uint32 (unsigned int x _EXC_MASKS_PARAM _EXC_INFO_PARAM) { #endif UINT128 res; res.w[HIGH_128W] = 0x3040000000000000ull; res.w[LOW_128W] = x; BID_RETURN (res); } #if DECIMAL_CALL_BY_REFERENCE void bid128_from_int64 (UINT128 * pres, SINT64 * px _EXC_MASKS_PARAM _EXC_INFO_PARAM) { SINT64 x = *px; #else UINT128 bid128_from_int64 (SINT64 x _EXC_MASKS_PARAM _EXC_INFO_PARAM) { #endif UINT128 res; // if integer is negative, use the absolute value if ((x & SIGNMASK64) == SIGNMASK64) { res.w[HIGH_128W] = 0xb040000000000000ull; res.w[LOW_128W] = ~x + 1; // 2's complement of x } else { res.w[HIGH_128W] = 0x3040000000000000ull; res.w[LOW_128W] = x; } BID_RETURN (res); } #if DECIMAL_CALL_BY_REFERENCE void bid128_from_uint64 (UINT128 * pres, UINT64 * px _EXC_MASKS_PARAM _EXC_INFO_PARAM) { UINT64 x = *px; #else UINT128 bid128_from_uint64 (UINT64 x _EXC_MASKS_PARAM _EXC_INFO_PARAM) { #endif UINT128 res; res.w[HIGH_128W] = 0x3040000000000000ull; res.w[LOW_128W] = x; BID_RETURN (res); }