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