/* IEEE floating point support routines, for GDB, the GNU Debugger.
Copyright (C) 1991-2024 Free Software Foundation, Inc.
This file is part of GDB.
This program 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 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
/* This is needed to pick up the NAN macro on some systems. */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <math.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
/* On some platforms, <float.h> provides DBL_QNAN. */
#ifdef STDC_HEADERS
#include <float.h>
#endif
/* The odds that CHAR_BIT will be anything but 8 are low enough that I'm not
going to bother with trying to muck around with whether it is defined in
a system header, what we do if not, etc. */
#define FLOATFORMAT_CHAR_BIT 8
static int floatformat_i387_ext_is_valid (const struct floatformat *fmt,
const void *from);
static int
floatformat_i387_ext_is_valid (const struct floatformat *fmt, const void *from)
{
/* In the i387 double-extended format, if the exponent is all ones,
then the integer bit must be set. If the exponent is neither 0
nor ~0, the intbit must also be set. Only if the exponent is
zero can it be zero, and then it must be zero. */
unsigned long exponent, int_bit;
const unsigned char *ufrom = (const unsigned char *) from;
/* A NaN is valid with any low part. */
if (top_nan)
return 1;
/* An infinity, zero or denormal requires low part 0 (positive or
negative). */
if ((unsigned long) top_exp == hfmt->exp_nan || top_exp == 0)
{
if (bot_exp != 0)
return 0;
return !mant_bits_set (hfmt, ufrom + 8);
}
/* The top part is now a finite normal value. The long double value
is the sum of the two parts, and the top part must equal the
result of rounding the long double value to nearest double. Thus
the bottom part must be <= 0.5ulp of the top part in absolute
value, and if it is < 0.5ulp then the long double is definitely
valid. */
if (bot_exp < top_exp - 53)
return 1;
if (bot_exp > top_exp - 53 && bot_exp != 0)
return 0;
if (bot_exp == 0)
{
/* The bottom part is 0 or denormal. Determine which, and if
denormal the first two set bits. */
int first_bit = -1, second_bit = -1, cur_bit;
for (cur_bit = 0; (unsigned int) cur_bit < hfmt->man_len; cur_bit++)
if (get_field (ufrom + 8, hfmt->byteorder, hfmt->totalsize,
hfmt->man_start + cur_bit, 1))
{
if (first_bit == -1)
first_bit = cur_bit;
else
{
second_bit = cur_bit;
break;
}
}
/* Bottom part 0 is OK. */
if (first_bit == -1)
return 1;
/* The real exponent of the bottom part is -first_bit. */
if (-first_bit < top_exp - 53)
return 1;
if (-first_bit > top_exp - 53)
return 0;
/* The bottom part is at least 0.5ulp of the top part. For this
to be OK, the bottom part must be exactly 0.5ulp (i.e. no
more bits set) and the top part must have last bit 0. */
if (second_bit != -1)
return 0;
return !get_field (ufrom, hfmt->byteorder, hfmt->totalsize,
hfmt->man_start + hfmt->man_len - 1, 1);
}
else
{
/* The bottom part is at least 0.5ulp of the top part. For this
to be OK, it must be exactly 0.5ulp (i.e. no explicit bits
set) and the top part must have last bit 0. */
if (get_field (ufrom, hfmt->byteorder, hfmt->totalsize,
hfmt->man_start + hfmt->man_len - 1, 1))
return 0;
return !mant_bits_set (hfmt, ufrom + 8);
}
}
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
/* Return 1 if any bits are explicitly set in the mantissa of UFROM,
format FMT, 0 otherwise. */
static int
mant_bits_set (const struct floatformat *fmt, const unsigned char *ufrom)
{
unsigned int mant_bits, mant_off;
int mant_bits_left;
mant_off = fmt->man_start;
mant_bits_left = fmt->man_len;
while (mant_bits_left > 0)
{
mant_bits = min (mant_bits_left, 32);
/* Extract a field which starts at START and is LEN bits long. DATA and
TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER. */
static unsigned long
get_field (const unsigned char *data, enum floatformat_byteorders order,
unsigned int total_len, unsigned int start, unsigned int len)
{
unsigned long result = 0;
unsigned int cur_byte;
int lo_bit, hi_bit, cur_bitshift = 0;
int nextbyte = (order == floatformat_little) ? 1 : -1;
/* Start is in big-endian bit order! Fix that first. */
start = total_len - (start + len);
/* Start at the least significant part of the field. */
if (order == floatformat_little)
cur_byte = start / FLOATFORMAT_CHAR_BIT;
else
cur_byte = (total_len - start - 1) / FLOATFORMAT_CHAR_BIT;
/* If the exponent indicates a NaN, we don't have information to
decide what to do. So we handle it like IEEE, except that we
don't try to preserve the type of NaN. FIXME. */
if ((unsigned long) exponent == fmt->exp_nan)
{
int nan = mant_bits_set (fmt, ufrom);
/* On certain systems (such as GNU/Linux), the use of the
INFINITY macro below may generate a warning that cannot be
silenced due to a bug in GCC (PR preprocessor/11931). The
preprocessor fails to recognise the __extension__ keyword in
conjunction with the GNU/C99 extension for hexadecimal
floating point constants and will issue a warning when
compiling with -pedantic. */
if (nan)
dto = NAN;
else
#ifdef __vax__
dto = HUGE_VAL;
#else
dto = INFINITY;
#endif
if (get_field (ufrom, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1))
dto = -dto;
/* Set a field which starts at START and is LEN bits long. DATA and
TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER. */
static void
put_field (unsigned char *data, enum floatformat_byteorders order,
unsigned int total_len, unsigned int start, unsigned int len,
unsigned long stuff_to_put)
{
unsigned int cur_byte;
int lo_bit, hi_bit;
int nextbyte = (order == floatformat_little) ? 1 : -1;
/* Start is in big-endian bit order! Fix that first. */
start = total_len - (start + len);
/* Start at the least significant part of the field. */
if (order == floatformat_little)
cur_byte = start / FLOATFORMAT_CHAR_BIT;
else
cur_byte = (total_len - start - 1) / FLOATFORMAT_CHAR_BIT;
/* Split values are not handled specially, since a bottom half of
zero is correct for any value representable as double (in the
only supported case of split values). */
/* If negative, set the sign bit. */
if (dfrom < 0)
{
put_field (uto, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1, 1);
dfrom = -dfrom;
}
if (dfrom == 0)
{
/* 0.0. */
return;
}
if (dfrom != dfrom)
{
/* NaN. */
put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
fmt->exp_len, fmt->exp_nan);
/* Be sure it's not infinity, but NaN value is irrelevant. */
put_field (uto, fmt->byteorder, fmt->totalsize, fmt->man_start,
32, 1);
return;
}
if (dfrom + dfrom == dfrom)
{
/* This can only happen for an infinite value (or zero, which we
already handled above). */
put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
fmt->exp_len, fmt->exp_nan);
return;
}
mant = frexp (dfrom, &exponent);
if (exponent + fmt->exp_bias - 1 > 0)
put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
fmt->exp_len, exponent + fmt->exp_bias - 1);
else
{
/* Handle a denormalized number. FIXME: What should we do for
non-IEEE formats? */
put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
fmt->exp_len, 0);
mant = ldexp (mant, exponent + fmt->exp_bias - 1);
}
/* If the integer bit is implicit, and we are not creating a
denormalized number, then we need to discard it. */
if ((unsigned int) mant_bits_left == fmt->man_len
&& fmt->intbit == floatformat_intbit_no
&& exponent + fmt->exp_bias - 1 > 0)
{
mant_long &= 0x7fffffff;
mant_bits -= 1;
}
else if (mant_bits < 32)
{
/* The bits we want are in the most significant MANT_BITS bits of
mant_long. Move them to the least significant. */
mant_long >>= 32 - mant_bits;
}
#if IEEE_DEBUG > 1
/* This is to be run on a host which uses 68881 format. */
{
long double ex = *(long double *)exten;
if (ex != n)
printf ("Differ(from vs. extended): %.20g\n", n);
}
#endif
}