133965Sjdp/* IEEE floating point support routines, for GDB, the GNU Debugger.
2218822Sdim   Copyright 1991, 1994, 1999, 2000, 2003, 2005, 2006
3218822Sdim   Free Software Foundation, Inc.
433965Sjdp
533965SjdpThis file is part of GDB.
633965Sjdp
733965SjdpThis program is free software; you can redistribute it and/or modify
833965Sjdpit under the terms of the GNU General Public License as published by
933965Sjdpthe Free Software Foundation; either version 2 of the License, or
1033965Sjdp(at your option) any later version.
1133965Sjdp
1233965SjdpThis program is distributed in the hope that it will be useful,
1333965Sjdpbut WITHOUT ANY WARRANTY; without even the implied warranty of
1433965SjdpMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1533965SjdpGNU General Public License for more details.
1633965Sjdp
1733965SjdpYou should have received a copy of the GNU General Public License
1833965Sjdpalong with this program; if not, write to the Free Software
19218822SdimFoundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
2033965Sjdp
21130561Sobrien/* This is needed to pick up the NAN macro on some systems.  */
22130561Sobrien#define _GNU_SOURCE
23130561Sobrien
24130561Sobrien#ifdef HAVE_CONFIG_H
25130561Sobrien#include "config.h"
26130561Sobrien#endif
27130561Sobrien
28130561Sobrien#include <math.h>
29130561Sobrien
30130561Sobrien#ifdef HAVE_STRING_H
31130561Sobrien#include <string.h>
32130561Sobrien#endif
33130561Sobrien
34218822Sdim/* On some platforms, <float.h> provides DBL_QNAN.  */
35218822Sdim#ifdef STDC_HEADERS
36218822Sdim#include <float.h>
37218822Sdim#endif
38218822Sdim
39130561Sobrien#include "ansidecl.h"
40130561Sobrien#include "libiberty.h"
4133965Sjdp#include "floatformat.h"
42130561Sobrien
43130561Sobrien#ifndef INFINITY
44130561Sobrien#ifdef HUGE_VAL
45130561Sobrien#define INFINITY HUGE_VAL
4633965Sjdp#else
47130561Sobrien#define INFINITY (1.0 / 0.0)
4833965Sjdp#endif
49130561Sobrien#endif
5033965Sjdp
51130561Sobrien#ifndef NAN
52218822Sdim#ifdef DBL_QNAN
53218822Sdim#define NAN DBL_QNAN
54218822Sdim#else
55130561Sobrien#define NAN (0.0 / 0.0)
56130561Sobrien#endif
57218822Sdim#endif
58130561Sobrien
59218822Sdimstatic unsigned long get_field (const unsigned char *,
60218822Sdim                                enum floatformat_byteorders,
61218822Sdim                                unsigned int,
62218822Sdim                                unsigned int,
63218822Sdim                                unsigned int);
64218822Sdimstatic int floatformat_always_valid (const struct floatformat *fmt,
65218822Sdim                                     const void *from);
66130561Sobrien
67130561Sobrienstatic int
68218822Sdimfloatformat_always_valid (const struct floatformat *fmt ATTRIBUTE_UNUSED,
69218822Sdim                          const void *from ATTRIBUTE_UNUSED)
70130561Sobrien{
71130561Sobrien  return 1;
72130561Sobrien}
73130561Sobrien
7433965Sjdp/* The odds that CHAR_BIT will be anything but 8 are low enough that I'm not
7533965Sjdp   going to bother with trying to muck around with whether it is defined in
7633965Sjdp   a system header, what we do if not, etc.  */
7733965Sjdp#define FLOATFORMAT_CHAR_BIT 8
7833965Sjdp
7933965Sjdp/* floatformats for IEEE single and double, big and little endian.  */
8033965Sjdpconst struct floatformat floatformat_ieee_single_big =
8133965Sjdp{
8277298Sobrien  floatformat_big, 32, 0, 1, 8, 127, 255, 9, 23,
8377298Sobrien  floatformat_intbit_no,
84130561Sobrien  "floatformat_ieee_single_big",
85130561Sobrien  floatformat_always_valid
8633965Sjdp};
8733965Sjdpconst struct floatformat floatformat_ieee_single_little =
8833965Sjdp{
8977298Sobrien  floatformat_little, 32, 0, 1, 8, 127, 255, 9, 23,
9077298Sobrien  floatformat_intbit_no,
91130561Sobrien  "floatformat_ieee_single_little",
92130561Sobrien  floatformat_always_valid
9333965Sjdp};
9433965Sjdpconst struct floatformat floatformat_ieee_double_big =
9533965Sjdp{
9677298Sobrien  floatformat_big, 64, 0, 1, 11, 1023, 2047, 12, 52,
9777298Sobrien  floatformat_intbit_no,
98130561Sobrien  "floatformat_ieee_double_big",
99130561Sobrien  floatformat_always_valid
10033965Sjdp};
10133965Sjdpconst struct floatformat floatformat_ieee_double_little =
10233965Sjdp{
10377298Sobrien  floatformat_little, 64, 0, 1, 11, 1023, 2047, 12, 52,
10477298Sobrien  floatformat_intbit_no,
105130561Sobrien  "floatformat_ieee_double_little",
106130561Sobrien  floatformat_always_valid
10733965Sjdp};
10833965Sjdp
10938889Sjdp/* floatformat for IEEE double, little endian byte order, with big endian word
11038889Sjdp   ordering, as on the ARM.  */
11138889Sjdp
11238889Sjdpconst struct floatformat floatformat_ieee_double_littlebyte_bigword =
11338889Sjdp{
11477298Sobrien  floatformat_littlebyte_bigword, 64, 0, 1, 11, 1023, 2047, 12, 52,
11577298Sobrien  floatformat_intbit_no,
116130561Sobrien  "floatformat_ieee_double_littlebyte_bigword",
117130561Sobrien  floatformat_always_valid
11838889Sjdp};
11938889Sjdp
120218822Sdim/* floatformat for VAX.  Not quite IEEE, but close enough.  */
121130561Sobrien
122218822Sdimconst struct floatformat floatformat_vax_f =
123218822Sdim{
124218822Sdim  floatformat_vax, 32, 0, 1, 8, 129, 0, 9, 23,
125218822Sdim  floatformat_intbit_no,
126218822Sdim  "floatformat_vax_f",
127218822Sdim  floatformat_always_valid
128218822Sdim};
129218822Sdimconst struct floatformat floatformat_vax_d =
130218822Sdim{
131218822Sdim  floatformat_vax, 64, 0, 1, 8, 129, 0, 9, 55,
132218822Sdim  floatformat_intbit_no,
133218822Sdim  "floatformat_vax_d",
134218822Sdim  floatformat_always_valid
135218822Sdim};
136218822Sdimconst struct floatformat floatformat_vax_g =
137218822Sdim{
138218822Sdim  floatformat_vax, 64, 0, 1, 11, 1025, 0, 12, 52,
139218822Sdim  floatformat_intbit_no,
140218822Sdim  "floatformat_vax_g",
141218822Sdim  floatformat_always_valid
142218822Sdim};
143218822Sdim
144218822Sdimstatic int floatformat_i387_ext_is_valid (const struct floatformat *fmt,
145218822Sdim					  const void *from);
146218822Sdim
147130561Sobrienstatic int
148218822Sdimfloatformat_i387_ext_is_valid (const struct floatformat *fmt, const void *from)
149130561Sobrien{
150130561Sobrien  /* In the i387 double-extended format, if the exponent is all ones,
151130561Sobrien     then the integer bit must be set.  If the exponent is neither 0
152130561Sobrien     nor ~0, the intbit must also be set.  Only if the exponent is
153130561Sobrien     zero can it be zero, and then it must be zero.  */
154130561Sobrien  unsigned long exponent, int_bit;
155130561Sobrien  const unsigned char *ufrom = (const unsigned char *) from;
156218822Sdim
157130561Sobrien  exponent = get_field (ufrom, fmt->byteorder, fmt->totalsize,
158130561Sobrien			fmt->exp_start, fmt->exp_len);
159130561Sobrien  int_bit = get_field (ufrom, fmt->byteorder, fmt->totalsize,
160130561Sobrien		       fmt->man_start, 1);
161218822Sdim
162130561Sobrien  if ((exponent == 0) != (int_bit == 0))
163130561Sobrien    return 0;
164130561Sobrien  else
165130561Sobrien    return 1;
166130561Sobrien}
167130561Sobrien
16833965Sjdpconst struct floatformat floatformat_i387_ext =
16933965Sjdp{
17033965Sjdp  floatformat_little, 80, 0, 1, 15, 0x3fff, 0x7fff, 16, 64,
17177298Sobrien  floatformat_intbit_yes,
172130561Sobrien  "floatformat_i387_ext",
173130561Sobrien  floatformat_i387_ext_is_valid
17433965Sjdp};
17533965Sjdpconst struct floatformat floatformat_m68881_ext =
17633965Sjdp{
17733965Sjdp  /* Note that the bits from 16 to 31 are unused.  */
17877298Sobrien  floatformat_big, 96, 0, 1, 15, 0x3fff, 0x7fff, 32, 64,
17977298Sobrien  floatformat_intbit_yes,
180130561Sobrien  "floatformat_m68881_ext",
181130561Sobrien  floatformat_always_valid
18233965Sjdp};
18333965Sjdpconst struct floatformat floatformat_i960_ext =
18433965Sjdp{
18533965Sjdp  /* Note that the bits from 0 to 15 are unused.  */
18633965Sjdp  floatformat_little, 96, 16, 17, 15, 0x3fff, 0x7fff, 32, 64,
18777298Sobrien  floatformat_intbit_yes,
188130561Sobrien  "floatformat_i960_ext",
189130561Sobrien  floatformat_always_valid
19033965Sjdp};
19133965Sjdpconst struct floatformat floatformat_m88110_ext =
19233965Sjdp{
19389857Sobrien  floatformat_big, 80, 0, 1, 15, 0x3fff, 0x7fff, 16, 64,
19489857Sobrien  floatformat_intbit_yes,
195130561Sobrien  "floatformat_m88110_ext",
196130561Sobrien  floatformat_always_valid
19789857Sobrien};
19889857Sobrienconst struct floatformat floatformat_m88110_harris_ext =
19989857Sobrien{
20033965Sjdp  /* Harris uses raw format 128 bytes long, but the number is just an ieee
20133965Sjdp     double, and the last 64 bits are wasted. */
20233965Sjdp  floatformat_big,128, 0, 1, 11,  0x3ff,  0x7ff, 12, 52,
20377298Sobrien  floatformat_intbit_no,
204130561Sobrien  "floatformat_m88110_ext_harris",
205130561Sobrien  floatformat_always_valid
20633965Sjdp};
20789857Sobrienconst struct floatformat floatformat_arm_ext_big =
20889857Sobrien{
20989857Sobrien  /* Bits 1 to 16 are unused.  */
21089857Sobrien  floatformat_big, 96, 0, 17, 15, 0x3fff, 0x7fff, 32, 64,
21189857Sobrien  floatformat_intbit_yes,
212130561Sobrien  "floatformat_arm_ext_big",
213130561Sobrien  floatformat_always_valid
21489857Sobrien};
21589857Sobrienconst struct floatformat floatformat_arm_ext_littlebyte_bigword =
21689857Sobrien{
21789857Sobrien  /* Bits 1 to 16 are unused.  */
21889857Sobrien  floatformat_littlebyte_bigword, 96, 0, 17, 15, 0x3fff, 0x7fff, 32, 64,
21989857Sobrien  floatformat_intbit_yes,
220130561Sobrien  "floatformat_arm_ext_littlebyte_bigword",
221130561Sobrien  floatformat_always_valid
22289857Sobrien};
22389857Sobrienconst struct floatformat floatformat_ia64_spill_big =
22489857Sobrien{
22589857Sobrien  floatformat_big, 128, 0, 1, 17, 65535, 0x1ffff, 18, 64,
22689857Sobrien  floatformat_intbit_yes,
227130561Sobrien  "floatformat_ia64_spill_big",
228130561Sobrien  floatformat_always_valid
22989857Sobrien};
23089857Sobrienconst struct floatformat floatformat_ia64_spill_little =
23189857Sobrien{
23289857Sobrien  floatformat_little, 128, 0, 1, 17, 65535, 0x1ffff, 18, 64,
23389857Sobrien  floatformat_intbit_yes,
234130561Sobrien  "floatformat_ia64_spill_little",
235130561Sobrien  floatformat_always_valid
23689857Sobrien};
23789857Sobrienconst struct floatformat floatformat_ia64_quad_big =
23889857Sobrien{
23989857Sobrien  floatformat_big, 128, 0, 1, 15, 16383, 0x7fff, 16, 112,
24089857Sobrien  floatformat_intbit_no,
241130561Sobrien  "floatformat_ia64_quad_big",
242130561Sobrien  floatformat_always_valid
24389857Sobrien};
24489857Sobrienconst struct floatformat floatformat_ia64_quad_little =
24589857Sobrien{
24689857Sobrien  floatformat_little, 128, 0, 1, 15, 16383, 0x7fff, 16, 112,
24789857Sobrien  floatformat_intbit_no,
248130561Sobrien  "floatformat_ia64_quad_little",
249130561Sobrien  floatformat_always_valid
25089857Sobrien};
25133965Sjdp
252218822Sdim
253218822Sdim#ifndef min
254218822Sdim#define min(a, b) ((a) < (b) ? (a) : (b))
255218822Sdim#endif
256218822Sdim
257130561Sobrien/* Extract a field which starts at START and is LEN bits long.  DATA and
25833965Sjdp   TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER.  */
25933965Sjdpstatic unsigned long
260218822Sdimget_field (const unsigned char *data, enum floatformat_byteorders order,
261218822Sdim           unsigned int total_len, unsigned int start, unsigned int len)
26233965Sjdp{
263218822Sdim  unsigned long result = 0;
26433965Sjdp  unsigned int cur_byte;
265218822Sdim  int lo_bit, hi_bit, cur_bitshift = 0;
266218822Sdim  int nextbyte = (order == floatformat_little) ? 1 : -1;
26733965Sjdp
268218822Sdim  /* Start is in big-endian bit order!  Fix that first.  */
269218822Sdim  start = total_len - (start + len);
270218822Sdim
27133965Sjdp  /* Start at the least significant part of the field.  */
27233965Sjdp  if (order == floatformat_little)
273218822Sdim    cur_byte = start / FLOATFORMAT_CHAR_BIT;
27433965Sjdp  else
275218822Sdim    cur_byte = (total_len - start - 1) / FLOATFORMAT_CHAR_BIT;
27633965Sjdp
277218822Sdim  lo_bit = start % FLOATFORMAT_CHAR_BIT;
278218822Sdim  hi_bit = min (lo_bit + len, FLOATFORMAT_CHAR_BIT);
279218822Sdim
280218822Sdim  do
28133965Sjdp    {
282218822Sdim      unsigned int shifted = *(data + cur_byte) >> lo_bit;
283218822Sdim      unsigned int bits = hi_bit - lo_bit;
284218822Sdim      unsigned int mask = (1 << bits) - 1;
285218822Sdim      result |= (shifted & mask) << cur_bitshift;
286218822Sdim      len -= bits;
287218822Sdim      cur_bitshift += bits;
288218822Sdim      cur_byte += nextbyte;
289218822Sdim      lo_bit = 0;
290218822Sdim      hi_bit = min (len, FLOATFORMAT_CHAR_BIT);
29133965Sjdp    }
292218822Sdim  while (len != 0);
293218822Sdim
29433965Sjdp  return result;
29533965Sjdp}
29633965Sjdp
29733965Sjdp/* Convert from FMT to a double.
29833965Sjdp   FROM is the address of the extended float.
29933965Sjdp   Store the double in *TO.  */
30033965Sjdp
30133965Sjdpvoid
302218822Sdimfloatformat_to_double (const struct floatformat *fmt,
303218822Sdim                       const void *from, double *to)
30433965Sjdp{
305218822Sdim  const unsigned char *ufrom = (const unsigned char *) from;
30633965Sjdp  double dto;
30733965Sjdp  long exponent;
30833965Sjdp  unsigned long mant;
30933965Sjdp  unsigned int mant_bits, mant_off;
31033965Sjdp  int mant_bits_left;
31133965Sjdp  int special_exponent;		/* It's a NaN, denorm or zero */
31233965Sjdp
31333965Sjdp  exponent = get_field (ufrom, fmt->byteorder, fmt->totalsize,
31433965Sjdp			fmt->exp_start, fmt->exp_len);
31533965Sjdp
316130561Sobrien  /* If the exponent indicates a NaN, we don't have information to
317130561Sobrien     decide what to do.  So we handle it like IEEE, except that we
318130561Sobrien     don't try to preserve the type of NaN.  FIXME.  */
319130561Sobrien  if ((unsigned long) exponent == fmt->exp_nan)
320130561Sobrien    {
321130561Sobrien      int nan;
322130561Sobrien
323130561Sobrien      mant_off = fmt->man_start;
324130561Sobrien      mant_bits_left = fmt->man_len;
325130561Sobrien      nan = 0;
326130561Sobrien      while (mant_bits_left > 0)
327130561Sobrien	{
328130561Sobrien	  mant_bits = min (mant_bits_left, 32);
329130561Sobrien
330130561Sobrien	  if (get_field (ufrom, fmt->byteorder, fmt->totalsize,
331130561Sobrien			 mant_off, mant_bits) != 0)
332130561Sobrien	    {
333130561Sobrien	      /* This is a NaN.  */
334130561Sobrien	      nan = 1;
335130561Sobrien	      break;
336130561Sobrien	    }
337130561Sobrien
338130561Sobrien	  mant_off += mant_bits;
339130561Sobrien	  mant_bits_left -= mant_bits;
340130561Sobrien	}
341130561Sobrien
342218822Sdim      /* On certain systems (such as GNU/Linux), the use of the
343218822Sdim	 INFINITY macro below may generate a warning that can not be
344218822Sdim	 silenced due to a bug in GCC (PR preprocessor/11931).  The
345218822Sdim	 preprocessor fails to recognise the __extension__ keyword in
346218822Sdim	 conjunction with the GNU/C99 extension for hexadecimal
347218822Sdim	 floating point constants and will issue a warning when
348218822Sdim	 compiling with -pedantic.  */
349130561Sobrien      if (nan)
350130561Sobrien	dto = NAN;
351130561Sobrien      else
352130561Sobrien	dto = INFINITY;
353130561Sobrien
354130561Sobrien      if (get_field (ufrom, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1))
355130561Sobrien	dto = -dto;
356130561Sobrien
357130561Sobrien      *to = dto;
358130561Sobrien
359130561Sobrien      return;
360130561Sobrien    }
361130561Sobrien
36233965Sjdp  mant_bits_left = fmt->man_len;
36333965Sjdp  mant_off = fmt->man_start;
36433965Sjdp  dto = 0.0;
36533965Sjdp
36660484Sobrien  special_exponent = exponent == 0 || (unsigned long) exponent == fmt->exp_nan;
36733965Sjdp
36833965Sjdp  /* Don't bias zero's, denorms or NaNs.  */
36933965Sjdp  if (!special_exponent)
37033965Sjdp    exponent -= fmt->exp_bias;
37133965Sjdp
37233965Sjdp  /* Build the result algebraically.  Might go infinite, underflow, etc;
37333965Sjdp     who cares. */
37433965Sjdp
37533965Sjdp  /* If this format uses a hidden bit, explicitly add it in now.  Otherwise,
37633965Sjdp     increment the exponent by one to account for the integer bit.  */
37733965Sjdp
37833965Sjdp  if (!special_exponent)
37960484Sobrien    {
38060484Sobrien      if (fmt->intbit == floatformat_intbit_no)
38160484Sobrien	dto = ldexp (1.0, exponent);
38260484Sobrien      else
38360484Sobrien	exponent++;
38460484Sobrien    }
38533965Sjdp
38633965Sjdp  while (mant_bits_left > 0)
38733965Sjdp    {
38833965Sjdp      mant_bits = min (mant_bits_left, 32);
38933965Sjdp
39033965Sjdp      mant = get_field (ufrom, fmt->byteorder, fmt->totalsize,
39133965Sjdp			 mant_off, mant_bits);
39233965Sjdp
393130561Sobrien      /* Handle denormalized numbers.  FIXME: What should we do for
394130561Sobrien	 non-IEEE formats?  */
395218822Sdim      if (special_exponent && exponent == 0 && mant != 0)
396130561Sobrien	dto += ldexp ((double)mant,
397130561Sobrien		      (- fmt->exp_bias
398130561Sobrien		       - mant_bits
399130561Sobrien		       - (mant_off - fmt->man_start)
400130561Sobrien		       + 1));
401130561Sobrien      else
402130561Sobrien	dto += ldexp ((double)mant, exponent - mant_bits);
403130561Sobrien      if (exponent != 0)
404130561Sobrien	exponent -= mant_bits;
40533965Sjdp      mant_off += mant_bits;
40633965Sjdp      mant_bits_left -= mant_bits;
40733965Sjdp    }
40833965Sjdp
40933965Sjdp  /* Negate it if negative.  */
41033965Sjdp  if (get_field (ufrom, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1))
41133965Sjdp    dto = -dto;
41233965Sjdp  *to = dto;
41333965Sjdp}
41433965Sjdp
415218822Sdimstatic void put_field (unsigned char *, enum floatformat_byteorders,
416218822Sdim                       unsigned int,
417218822Sdim                       unsigned int,
418218822Sdim                       unsigned int,
419218822Sdim                       unsigned long);
42033965Sjdp
421130561Sobrien/* Set a field which starts at START and is LEN bits long.  DATA and
42233965Sjdp   TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER.  */
42333965Sjdpstatic void
424218822Sdimput_field (unsigned char *data, enum floatformat_byteorders order,
425218822Sdim           unsigned int total_len, unsigned int start, unsigned int len,
426218822Sdim           unsigned long stuff_to_put)
42733965Sjdp{
42833965Sjdp  unsigned int cur_byte;
429218822Sdim  int lo_bit, hi_bit;
430218822Sdim  int nextbyte = (order == floatformat_little) ? 1 : -1;
43133965Sjdp
432218822Sdim  /* Start is in big-endian bit order!  Fix that first.  */
433218822Sdim  start = total_len - (start + len);
434218822Sdim
43533965Sjdp  /* Start at the least significant part of the field.  */
43633965Sjdp  if (order == floatformat_little)
437218822Sdim    cur_byte = start / FLOATFORMAT_CHAR_BIT;
43833965Sjdp  else
439218822Sdim    cur_byte = (total_len - start - 1) / FLOATFORMAT_CHAR_BIT;
44033965Sjdp
441218822Sdim  lo_bit = start % FLOATFORMAT_CHAR_BIT;
442218822Sdim  hi_bit = min (lo_bit + len, FLOATFORMAT_CHAR_BIT);
443218822Sdim
444218822Sdim  do
44533965Sjdp    {
446218822Sdim      unsigned char *byte_ptr = data + cur_byte;
447218822Sdim      unsigned int bits = hi_bit - lo_bit;
448218822Sdim      unsigned int mask = ((1 << bits) - 1) << lo_bit;
449218822Sdim      *byte_ptr = (*byte_ptr & ~mask) | ((stuff_to_put << lo_bit) & mask);
450218822Sdim      stuff_to_put >>= bits;
451218822Sdim      len -= bits;
452218822Sdim      cur_byte += nextbyte;
453218822Sdim      lo_bit = 0;
454218822Sdim      hi_bit = min (len, FLOATFORMAT_CHAR_BIT);
45533965Sjdp    }
456218822Sdim  while (len != 0);
45733965Sjdp}
45833965Sjdp
45933965Sjdp/* The converse: convert the double *FROM to an extended float
46033965Sjdp   and store where TO points.  Neither FROM nor TO have any alignment
46133965Sjdp   restrictions.  */
46233965Sjdp
46333965Sjdpvoid
464218822Sdimfloatformat_from_double (const struct floatformat *fmt,
465218822Sdim                         const double *from, void *to)
46633965Sjdp{
46733965Sjdp  double dfrom;
46833965Sjdp  int exponent;
46933965Sjdp  double mant;
47033965Sjdp  unsigned int mant_bits, mant_off;
47133965Sjdp  int mant_bits_left;
472218822Sdim  unsigned char *uto = (unsigned char *) to;
47333965Sjdp
474130561Sobrien  dfrom = *from;
47533965Sjdp  memset (uto, 0, fmt->totalsize / FLOATFORMAT_CHAR_BIT);
476130561Sobrien
477130561Sobrien  /* If negative, set the sign bit.  */
478130561Sobrien  if (dfrom < 0)
479130561Sobrien    {
480130561Sobrien      put_field (uto, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1, 1);
481130561Sobrien      dfrom = -dfrom;
482130561Sobrien    }
483130561Sobrien
48433965Sjdp  if (dfrom == 0)
485130561Sobrien    {
486130561Sobrien      /* 0.0.  */
487130561Sobrien      return;
488130561Sobrien    }
489130561Sobrien
49033965Sjdp  if (dfrom != dfrom)
49133965Sjdp    {
492130561Sobrien      /* NaN.  */
49333965Sjdp      put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
49433965Sjdp		 fmt->exp_len, fmt->exp_nan);
495130561Sobrien      /* Be sure it's not infinity, but NaN value is irrelevant.  */
49633965Sjdp      put_field (uto, fmt->byteorder, fmt->totalsize, fmt->man_start,
49733965Sjdp		 32, 1);
49833965Sjdp      return;
49933965Sjdp    }
50033965Sjdp
501130561Sobrien  if (dfrom + dfrom == dfrom)
50233965Sjdp    {
503130561Sobrien      /* This can only happen for an infinite value (or zero, which we
504130561Sobrien	 already handled above).  */
505130561Sobrien      put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
506130561Sobrien		 fmt->exp_len, fmt->exp_nan);
507130561Sobrien      return;
50833965Sjdp    }
50933965Sjdp
51033965Sjdp  mant = frexp (dfrom, &exponent);
511130561Sobrien  if (exponent + fmt->exp_bias - 1 > 0)
512130561Sobrien    put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
513130561Sobrien	       fmt->exp_len, exponent + fmt->exp_bias - 1);
514130561Sobrien  else
515130561Sobrien    {
516130561Sobrien      /* Handle a denormalized number.  FIXME: What should we do for
517130561Sobrien	 non-IEEE formats?  */
518130561Sobrien      put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
519130561Sobrien		 fmt->exp_len, 0);
520130561Sobrien      mant = ldexp (mant, exponent + fmt->exp_bias - 1);
521130561Sobrien    }
52233965Sjdp
52333965Sjdp  mant_bits_left = fmt->man_len;
52433965Sjdp  mant_off = fmt->man_start;
52533965Sjdp  while (mant_bits_left > 0)
52633965Sjdp    {
52733965Sjdp      unsigned long mant_long;
52833965Sjdp      mant_bits = mant_bits_left < 32 ? mant_bits_left : 32;
52933965Sjdp
53033965Sjdp      mant *= 4294967296.0;
53133965Sjdp      mant_long = (unsigned long)mant;
53233965Sjdp      mant -= mant_long;
53333965Sjdp
534130561Sobrien      /* If the integer bit is implicit, and we are not creating a
535130561Sobrien	 denormalized number, then we need to discard it.  */
53660484Sobrien      if ((unsigned int) mant_bits_left == fmt->man_len
537130561Sobrien	  && fmt->intbit == floatformat_intbit_no
538130561Sobrien	  && exponent + fmt->exp_bias - 1 > 0)
53933965Sjdp	{
54033965Sjdp	  mant_long &= 0x7fffffff;
54133965Sjdp	  mant_bits -= 1;
54233965Sjdp	}
54333965Sjdp      else if (mant_bits < 32)
54433965Sjdp	{
54533965Sjdp	  /* The bits we want are in the most significant MANT_BITS bits of
54633965Sjdp	     mant_long.  Move them to the least significant.  */
54733965Sjdp	  mant_long >>= 32 - mant_bits;
54833965Sjdp	}
54933965Sjdp
55033965Sjdp      put_field (uto, fmt->byteorder, fmt->totalsize,
55133965Sjdp		 mant_off, mant_bits, mant_long);
55233965Sjdp      mant_off += mant_bits;
55333965Sjdp      mant_bits_left -= mant_bits;
55433965Sjdp    }
55533965Sjdp}
55633965Sjdp
557130561Sobrien/* Return non-zero iff the data at FROM is a valid number in format FMT.  */
55833965Sjdp
559130561Sobrienint
560218822Sdimfloatformat_is_valid (const struct floatformat *fmt, const void *from)
561130561Sobrien{
562130561Sobrien  return fmt->is_valid (fmt, from);
563130561Sobrien}
564130561Sobrien
565130561Sobrien
56633965Sjdp#ifdef IEEE_DEBUG
56733965Sjdp
568130561Sobrien#include <stdio.h>
569130561Sobrien
57033965Sjdp/* This is to be run on a host which uses IEEE floating point.  */
57133965Sjdp
57233965Sjdpvoid
573218822Sdimieee_test (double n)
57433965Sjdp{
57533965Sjdp  double result;
57633965Sjdp
577218822Sdim  floatformat_to_double (&floatformat_ieee_double_little, &n, &result);
578130561Sobrien  if ((n != result && (! isnan (n) || ! isnan (result)))
579130561Sobrien      || (n < 0 && result >= 0)
580130561Sobrien      || (n >= 0 && result < 0))
58133965Sjdp    printf ("Differ(to): %.20g -> %.20g\n", n, result);
582130561Sobrien
583218822Sdim  floatformat_from_double (&floatformat_ieee_double_little, &n, &result);
584130561Sobrien  if ((n != result && (! isnan (n) || ! isnan (result)))
585130561Sobrien      || (n < 0 && result >= 0)
586130561Sobrien      || (n >= 0 && result < 0))
58733965Sjdp    printf ("Differ(from): %.20g -> %.20g\n", n, result);
58833965Sjdp
589130561Sobrien#if 0
590130561Sobrien  {
591130561Sobrien    char exten[16];
59233965Sjdp
593130561Sobrien    floatformat_from_double (&floatformat_m68881_ext, &n, exten);
594130561Sobrien    floatformat_to_double (&floatformat_m68881_ext, exten, &result);
595130561Sobrien    if (n != result)
596130561Sobrien      printf ("Differ(to+from): %.20g -> %.20g\n", n, result);
597130561Sobrien  }
598130561Sobrien#endif
599130561Sobrien
60033965Sjdp#if IEEE_DEBUG > 1
60133965Sjdp  /* This is to be run on a host which uses 68881 format.  */
60233965Sjdp  {
60333965Sjdp    long double ex = *(long double *)exten;
60433965Sjdp    if (ex != n)
60533965Sjdp      printf ("Differ(from vs. extended): %.20g\n", n);
60633965Sjdp  }
60733965Sjdp#endif
60833965Sjdp}
60933965Sjdp
61033965Sjdpint
611218822Sdimmain (void)
61233965Sjdp{
613130561Sobrien  ieee_test (0.0);
61433965Sjdp  ieee_test (0.5);
61533965Sjdp  ieee_test (256.0);
61633965Sjdp  ieee_test (0.12345);
61733965Sjdp  ieee_test (234235.78907234);
61833965Sjdp  ieee_test (-512.0);
61933965Sjdp  ieee_test (-0.004321);
620130561Sobrien  ieee_test (1.2E-70);
621130561Sobrien  ieee_test (1.2E-316);
622130561Sobrien  ieee_test (4.9406564584124654E-324);
623130561Sobrien  ieee_test (- 4.9406564584124654E-324);
624130561Sobrien  ieee_test (- 0.0);
625130561Sobrien  ieee_test (- INFINITY);
626130561Sobrien  ieee_test (- NAN);
627130561Sobrien  ieee_test (INFINITY);
628130561Sobrien  ieee_test (NAN);
62933965Sjdp  return 0;
63033965Sjdp}
63133965Sjdp#endif
632