floatformat.c revision 33965
1/* IEEE floating point support routines, for GDB, the GNU Debugger.
2   Copyright (C) 1991, 1994 Free Software Foundation, Inc.
3
4This file is part of GDB.
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2 of the License, or
9(at your option) any later version.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20#include "floatformat.h"
21#include <math.h>		/* ldexp */
22#ifdef __STDC__
23#include <stddef.h>
24extern void *memcpy (void *s1, const void *s2, size_t n);
25extern void *memset (void *s, int c, size_t n);
26#else
27extern char *memcpy ();
28extern char *memset ();
29#endif
30
31/* The odds that CHAR_BIT will be anything but 8 are low enough that I'm not
32   going to bother with trying to muck around with whether it is defined in
33   a system header, what we do if not, etc.  */
34#define FLOATFORMAT_CHAR_BIT 8
35
36/* floatformats for IEEE single and double, big and little endian.  */
37const struct floatformat floatformat_ieee_single_big =
38{
39  floatformat_big, 32, 0, 1, 8, 127, 255, 9, 23, floatformat_intbit_no
40};
41const struct floatformat floatformat_ieee_single_little =
42{
43  floatformat_little, 32, 0, 1, 8, 127, 255, 9, 23, floatformat_intbit_no
44};
45const struct floatformat floatformat_ieee_double_big =
46{
47  floatformat_big, 64, 0, 1, 11, 1023, 2047, 12, 52, floatformat_intbit_no
48};
49const struct floatformat floatformat_ieee_double_little =
50{
51  floatformat_little, 64, 0, 1, 11, 1023, 2047, 12, 52, floatformat_intbit_no
52};
53
54const struct floatformat floatformat_i387_ext =
55{
56  floatformat_little, 80, 0, 1, 15, 0x3fff, 0x7fff, 16, 64,
57  floatformat_intbit_yes
58};
59const struct floatformat floatformat_m68881_ext =
60{
61  /* Note that the bits from 16 to 31 are unused.  */
62  floatformat_big, 96, 0, 1, 15, 0x3fff, 0x7fff, 32, 64, floatformat_intbit_yes
63};
64const struct floatformat floatformat_i960_ext =
65{
66  /* Note that the bits from 0 to 15 are unused.  */
67  floatformat_little, 96, 16, 17, 15, 0x3fff, 0x7fff, 32, 64,
68  floatformat_intbit_yes
69};
70const struct floatformat floatformat_m88110_ext =
71{
72#ifdef HARRIS_FLOAT_FORMAT
73  /* Harris uses raw format 128 bytes long, but the number is just an ieee
74     double, and the last 64 bits are wasted. */
75  floatformat_big,128, 0, 1, 11,  0x3ff,  0x7ff, 12, 52,
76  floatformat_intbit_no
77#else
78  floatformat_big, 80, 0, 1, 15, 0x3fff, 0x7fff, 16, 64,
79  floatformat_intbit_yes
80#endif /* HARRIS_FLOAT_FORMAT */
81};
82const struct floatformat floatformat_arm_ext =
83{
84  /* Bits 1 to 16 are unused.  */
85  floatformat_big, 96, 0, 17, 15, 0x3fff, 0x7fff, 32, 64,
86  floatformat_intbit_yes
87};
88
89static unsigned long get_field PARAMS ((unsigned char *,
90					enum floatformat_byteorders,
91					unsigned int,
92					unsigned int,
93					unsigned int));
94
95/* Extract a field which starts at START and is LEN bytes long.  DATA and
96   TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER.  */
97static unsigned long
98get_field (data, order, total_len, start, len)
99     unsigned char *data;
100     enum floatformat_byteorders order;
101     unsigned int total_len;
102     unsigned int start;
103     unsigned int len;
104{
105  unsigned long result;
106  unsigned int cur_byte;
107  int cur_bitshift;
108
109  /* Start at the least significant part of the field.  */
110  cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT;
111  if (order == floatformat_little)
112    cur_byte = (total_len / FLOATFORMAT_CHAR_BIT) - cur_byte - 1;
113  cur_bitshift =
114    ((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT;
115  result = *(data + cur_byte) >> (-cur_bitshift);
116  cur_bitshift += FLOATFORMAT_CHAR_BIT;
117  if (order == floatformat_little)
118    ++cur_byte;
119  else
120    --cur_byte;
121
122  /* Move towards the most significant part of the field.  */
123  while (cur_bitshift < len)
124    {
125      if (len - cur_bitshift < FLOATFORMAT_CHAR_BIT)
126	/* This is the last byte; zero out the bits which are not part of
127	   this field.  */
128	result |=
129	  (*(data + cur_byte) & ((1 << (len - cur_bitshift)) - 1))
130	    << cur_bitshift;
131      else
132	result |= *(data + cur_byte) << cur_bitshift;
133      cur_bitshift += FLOATFORMAT_CHAR_BIT;
134      if (order == floatformat_little)
135	++cur_byte;
136      else
137	--cur_byte;
138    }
139  return result;
140}
141
142#ifndef min
143#define min(a, b) ((a) < (b) ? (a) : (b))
144#endif
145
146/* Convert from FMT to a double.
147   FROM is the address of the extended float.
148   Store the double in *TO.  */
149
150void
151floatformat_to_double (fmt, from, to)
152     const struct floatformat *fmt;
153     char *from;
154     double *to;
155{
156  unsigned char *ufrom = (unsigned char *)from;
157  double dto;
158  long exponent;
159  unsigned long mant;
160  unsigned int mant_bits, mant_off;
161  int mant_bits_left;
162  int special_exponent;		/* It's a NaN, denorm or zero */
163
164  exponent = get_field (ufrom, fmt->byteorder, fmt->totalsize,
165			fmt->exp_start, fmt->exp_len);
166  /* Note that if exponent indicates a NaN, we can't really do anything useful
167     (not knowing if the host has NaN's, or how to build one).  So it will
168     end up as an infinity or something close; that is OK.  */
169
170  mant_bits_left = fmt->man_len;
171  mant_off = fmt->man_start;
172  dto = 0.0;
173
174  special_exponent = exponent == 0 || exponent == fmt->exp_nan;
175
176  /* Don't bias zero's, denorms or NaNs.  */
177  if (!special_exponent)
178    exponent -= fmt->exp_bias;
179
180  /* Build the result algebraically.  Might go infinite, underflow, etc;
181     who cares. */
182
183  /* If this format uses a hidden bit, explicitly add it in now.  Otherwise,
184     increment the exponent by one to account for the integer bit.  */
185
186  if (!special_exponent)
187    if (fmt->intbit == floatformat_intbit_no)
188      dto = ldexp (1.0, exponent);
189    else
190      exponent++;
191
192  while (mant_bits_left > 0)
193    {
194      mant_bits = min (mant_bits_left, 32);
195
196      mant = get_field (ufrom, fmt->byteorder, fmt->totalsize,
197			 mant_off, mant_bits);
198
199      dto += ldexp ((double)mant, exponent - mant_bits);
200      exponent -= mant_bits;
201      mant_off += mant_bits;
202      mant_bits_left -= mant_bits;
203    }
204
205  /* Negate it if negative.  */
206  if (get_field (ufrom, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1))
207    dto = -dto;
208  *to = dto;
209}
210
211static void put_field PARAMS ((unsigned char *, enum floatformat_byteorders,
212			       unsigned int,
213			       unsigned int,
214			       unsigned int,
215			       unsigned long));
216
217/* Set a field which starts at START and is LEN bytes long.  DATA and
218   TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER.  */
219static void
220put_field (data, order, total_len, start, len, stuff_to_put)
221     unsigned char *data;
222     enum floatformat_byteorders order;
223     unsigned int total_len;
224     unsigned int start;
225     unsigned int len;
226     unsigned long stuff_to_put;
227{
228  unsigned int cur_byte;
229  int cur_bitshift;
230
231  /* Start at the least significant part of the field.  */
232  cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT;
233  if (order == floatformat_little)
234    cur_byte = (total_len / FLOATFORMAT_CHAR_BIT) - cur_byte - 1;
235  cur_bitshift =
236    ((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT;
237  *(data + cur_byte) &=
238    ~(((1 << ((start + len) % FLOATFORMAT_CHAR_BIT)) - 1) << (-cur_bitshift));
239  *(data + cur_byte) |=
240    (stuff_to_put & ((1 << FLOATFORMAT_CHAR_BIT) - 1)) << (-cur_bitshift);
241  cur_bitshift += FLOATFORMAT_CHAR_BIT;
242  if (order == floatformat_little)
243    ++cur_byte;
244  else
245    --cur_byte;
246
247  /* Move towards the most significant part of the field.  */
248  while (cur_bitshift < len)
249    {
250      if (len - cur_bitshift < FLOATFORMAT_CHAR_BIT)
251	{
252	  /* This is the last byte.  */
253	  *(data + cur_byte) &=
254	    ~((1 << (len - cur_bitshift)) - 1);
255	  *(data + cur_byte) |= (stuff_to_put >> cur_bitshift);
256	}
257      else
258	*(data + cur_byte) = ((stuff_to_put >> cur_bitshift)
259			      & ((1 << FLOATFORMAT_CHAR_BIT) - 1));
260      cur_bitshift += FLOATFORMAT_CHAR_BIT;
261      if (order == floatformat_little)
262	++cur_byte;
263      else
264	--cur_byte;
265    }
266}
267
268/* The converse: convert the double *FROM to an extended float
269   and store where TO points.  Neither FROM nor TO have any alignment
270   restrictions.  */
271
272void
273floatformat_from_double (fmt, from, to)
274     CONST struct floatformat *fmt;
275     double *from;
276     char *to;
277{
278  double dfrom;
279  int exponent;
280  double mant;
281  unsigned int mant_bits, mant_off;
282  int mant_bits_left;
283  unsigned char *uto = (unsigned char *)to;
284
285  memcpy (&dfrom, from, sizeof (dfrom));
286  memset (uto, 0, fmt->totalsize / FLOATFORMAT_CHAR_BIT);
287  if (dfrom == 0)
288    return;			/* Result is zero */
289  if (dfrom != dfrom)
290    {
291      /* From is NaN */
292      put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
293		 fmt->exp_len, fmt->exp_nan);
294      /* Be sure it's not infinity, but NaN value is irrel */
295      put_field (uto, fmt->byteorder, fmt->totalsize, fmt->man_start,
296		 32, 1);
297      return;
298    }
299
300  /* If negative, set the sign bit.  */
301  if (dfrom < 0)
302    {
303      put_field (uto, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1, 1);
304      dfrom = -dfrom;
305    }
306
307  /* How to tell an infinity from an ordinary number?  FIXME-someday */
308
309  mant = frexp (dfrom, &exponent);
310  put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start, fmt->exp_len,
311	     exponent + fmt->exp_bias - 1);
312
313  mant_bits_left = fmt->man_len;
314  mant_off = fmt->man_start;
315  while (mant_bits_left > 0)
316    {
317      unsigned long mant_long;
318      mant_bits = mant_bits_left < 32 ? mant_bits_left : 32;
319
320      mant *= 4294967296.0;
321      mant_long = (unsigned long)mant;
322      mant -= mant_long;
323
324      /* If the integer bit is implicit, then we need to discard it.
325	 If we are discarding a zero, we should be (but are not) creating
326	 a denormalized	number which means adjusting the exponent
327	 (I think).  */
328      if (mant_bits_left == fmt->man_len
329	  && fmt->intbit == floatformat_intbit_no)
330	{
331	  mant_long &= 0x7fffffff;
332	  mant_bits -= 1;
333	}
334      else if (mant_bits < 32)
335	{
336	  /* The bits we want are in the most significant MANT_BITS bits of
337	     mant_long.  Move them to the least significant.  */
338	  mant_long >>= 32 - mant_bits;
339	}
340
341      put_field (uto, fmt->byteorder, fmt->totalsize,
342		 mant_off, mant_bits, mant_long);
343      mant_off += mant_bits;
344      mant_bits_left -= mant_bits;
345    }
346}
347
348
349#ifdef IEEE_DEBUG
350
351/* This is to be run on a host which uses IEEE floating point.  */
352
353void
354ieee_test (n)
355     double n;
356{
357  double result;
358  char exten[16];
359
360  floatformat_to_double (&floatformat_ieee_double_big, &n, &result);
361  if (n != result)
362    printf ("Differ(to): %.20g -> %.20g\n", n, result);
363  floatformat_from_double (&floatformat_ieee_double_big, &n, &result);
364  if (n != result)
365    printf ("Differ(from): %.20g -> %.20g\n", n, result);
366
367  floatformat_from_double (&floatformat_m68881_ext, &n, exten);
368  floatformat_to_double (&floatformat_m68881_ext, exten, &result);
369  if (n != result)
370    printf ("Differ(to+from): %.20g -> %.20g\n", n, result);
371
372#if IEEE_DEBUG > 1
373  /* This is to be run on a host which uses 68881 format.  */
374  {
375    long double ex = *(long double *)exten;
376    if (ex != n)
377      printf ("Differ(from vs. extended): %.20g\n", n);
378  }
379#endif
380}
381
382int
383main ()
384{
385  ieee_test (0.5);
386  ieee_test (256.0);
387  ieee_test (0.12345);
388  ieee_test (234235.78907234);
389  ieee_test (-512.0);
390  ieee_test (-0.004321);
391  return 0;
392}
393#endif
394