ieee80.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /***************************************************************\
  2. * IEEE80.c *
  3. * Convert between "double" and IEEE 80 bit format *
  4. * in machine independent manner. *
  5. * Assumes array of char is a continuous region of 8 bit frames*
  6. * Assumes (unsigned long) has 32 useable bits*
  7. * billg, dpwe @media.mit.edu*
  8. * 01aug91*
  9. * 19aug91 aldel/dpwe workaround top bit problem in Ultrix*
  10. * cc's double->ulong cast*
  11. * 05feb92 dpwe/billg workaround top bit problem in*
  12. * THINKC4 + 68881 casting*
  13. \***************************************************************/
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <math.h>
  17. #include "ieee80.h"
  18. /* #define MAIN 1 to compile test routines */
  19. #define ULPOW2TO31 ((unsigned int)0x80000000L)
  20. #define DPOW2TO31 ((double)2147483648.0) /* 2^31 */
  21. /* have to deal with ulong's 32nd bit conditionally as double<->ulong casts
  22. don't work in some C compilers */
  23. static double myUlongToDouble (unsigned long ul);
  24. static unsigned int myDoubleToUlong (double val);
  25. static double myUlongToDouble(unsigned long ul)
  26. {
  27. double val;
  28. /* in THINK_C, ulong -> double apparently goes via long, so can only
  29. apply to 31 bit numbers. If 32nd bit is set, explicitly add on its
  30. value */
  31. if(ul & ULPOW2TO31)
  32. val = DPOW2TO31 + (ul & (~ULPOW2TO31));
  33. else
  34. val = ul;
  35. return val;
  36. }
  37. static unsigned int myDoubleToUlong(double val)
  38. {
  39. unsigned int ul;
  40. #ifdef _DEBUG
  41. /* cannot cast negative numbers into unsigned longs */
  42. if(val < 0)
  43. {
  44. fprintf(stderr,"IEEE80:DoubleToUlong: val < 0\n");
  45. }
  46. #endif
  47. /* in ultrix 4.1's cc, double -> unsigned long loses the top bit,
  48. so we do the conversion only on the bottom 31 bits and set the
  49. last one by hand, if val is truly that big */
  50. /* should maybe test for val > (double)(unsigned long)0xFFFFFFFF ? */
  51. if(val < DPOW2TO31)
  52. ul = (unsigned int)val;
  53. else
  54. ul = ULPOW2TO31 | (unsigned int)(val-DPOW2TO31);
  55. return ul;
  56. }
  57. /*
  58. * Convert IEEE 80 bit floating point to double.
  59. * Should be portable to all C compilers.
  60. */
  61. double ieee_80_to_double(unsigned char *p)
  62. {
  63. char sign;
  64. short exp = 0;
  65. unsigned int mant1 = 0;
  66. unsigned int mant0 = 0;
  67. double val;
  68. exp = *p++;
  69. exp <<= 8;
  70. exp |= *p++;
  71. sign = (exp & 0x8000) ? 1 : 0;
  72. exp &= 0x7FFF;
  73. mant1 = *p++;
  74. mant1 <<= 8;
  75. mant1 |= *p++;
  76. mant1 <<= 8;
  77. mant1 |= *p++;
  78. mant1 <<= 8;
  79. mant1 |= *p++;
  80. mant0 = *p++;
  81. mant0 <<= 8;
  82. mant0 |= *p++;
  83. mant0 <<= 8;
  84. mant0 |= *p++;
  85. mant0 <<= 8;
  86. mant0 |= *p++;
  87. /* special test for all bits zero meaning zero
  88. - else pow(2,-16383) bombs */
  89. if(mant1 == 0 && mant0 == 0 && exp == 0 && sign == 0)
  90. return 0.0;
  91. else{
  92. val = myUlongToDouble(mant0) * pow(2.0,-63.0);
  93. val += myUlongToDouble(mant1) * pow(2.0,-31.0);
  94. val *= pow(2.0,((double) exp) - 16383.0);
  95. return sign ? -val : val;
  96. }
  97. }
  98. /*
  99. * Convert double to IEEE 80 bit floating point
  100. * Should be portable to all C compilers.
  101. * 19aug91 aldel/dpwe covered for MSB bug in Ultrix 'cc'
  102. */
  103. void double_to_ieee_80(double val,unsigned char *p)
  104. {
  105. char sign = 0;
  106. short exp = 0;
  107. unsigned int mant1 = 0;
  108. unsigned int mant0 = 0;
  109. if(val < 0.0) { sign = 1; val = -val; }
  110. if(val != 0.0) /* val identically zero -> all elements zero */
  111. {
  112. exp = (short)(log(val)/log(2.0) + 16383.0);
  113. val *= pow(2.0, 31.0+16383.0-(double)exp);
  114. mant1 = myDoubleToUlong(val);
  115. val -= myUlongToDouble(mant1);
  116. val *= pow(2.0, 32.0);
  117. mant0 = myDoubleToUlong(val);
  118. }
  119. *p++ = ((sign<<7)|(exp>>8));
  120. *p++ = 0xFF & exp;
  121. *p++ = (char)(0xFF & (mant1>>24));
  122. *p++ = (char)(0xFF & (mant1>>16));
  123. *p++ = (char)(0xFF & (mant1>> 8));
  124. *p++ = (char)(0xFF & (mant1));
  125. *p++ = (char)(0xFF & (mant0>>24));
  126. *p++ = (char)(0xFF & (mant0>>16));
  127. *p++ = (char)(0xFF & (mant0>> 8));
  128. *p++ = (char)(0xFF & (mant0));
  129. }