gcc/libgcc/config/libbid/bid_from_int.c

355 lines
11 KiB
C

/* 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);
}