home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / forth / compiler / fpc / doc / sfloat.txt < prev    next >
Text File  |  1988-07-16  |  15KB  |  302 lines

  1.  
  2.                         Implementation Notes for SFLOAT,
  3.                         a software Floating Point package
  4.                               for 80X8X processors
  5.  
  6.                                 By Robert L. Smith
  7.  
  8. INTRODUCTION:
  9.  
  10.         This document contains various notes, thoughts, methods, etc. which
  11. relate to the software floating-point package which I have called SFLOAT.
  12. My main motivation for writing such a package is to help eliminate a common
  13. complaint about FORTH, that it isn't useful because it doesn't have floating
  14. point.  There are various possible answers to that complaint.  One answer is
  15. that floating point is available in most commercial FORTH systems.  On the
  16. other hand, most people initially try FORTH in a public domain package, such
  17. as F-83.  In that case, they will probably not find a floating point package.
  18. (One exception is a hardware floating point package for the PC clones.
  19. Another exception is a low precision "Zen" floating point published by Martin
  20. Tracy.)  Another answer to the complaint is that floating point is not needed.
  21. In a pure sense, that is certainly true.  However, there are many times when
  22. the human effort required to do a problem in fixed point arithmetic becomes
  23. unreasonable for the problem at hand.  In my particular case, I once wasted a
  24. great deal of time trying to approximate, in fixed point, a function involving
  25. the probability integral.  The problem was trivial in floating point.
  26.  
  27.         The floating point routines in this package are written for the most
  28. part in assembly language (CODE) routines for a specific processor type, the
  29. infamous Intel 8088 family.  Why did I write routines in code rather than in
  30. high level FORTH?  For the usual reason of speed.  In addition, detailed
  31. arithmetic functions are somewhat easier to write in code rather than high
  32. level because of the absence of the carry bit in FORTH.  I chose the Intel
  33. series because of its popularity.  An equivalent package for the Motorola
  34. 68XXX series should be much easier to write because of the 32 bit architect-
  35. ure.  I will leave that to others.
  36.  
  37.         It was my intent to produce a set of floating point routines which
  38. are both fast and accurate.  If you find errors or places where I can improve
  39. things, please let me know.
  40.  
  41.  
  42. REPRESENTATION:
  43.  
  44.         I was a member of the IEEE P754 committee on Floating Point.  At one
  45. point I was strongly inclined to use the IEEE floating point representation.
  46. There were two reasons why I rejected it.  The first reason was that I tried,
  47. and found it very difficult to work with.  The second reason is that someone
  48. else had built a floating point package around the IEEE format (Philip Koopman,
  49. Jr., MVP FORTH).  According to reports from Glenn Haydon, the routines were
  50. painfully slow.
  51.  
  52.         Every representation method has its advantages and disadvantages.
  53. I chose a 48 bit representation.  The most significant bit is the sign bit.
  54. The next 15 bits are used to indicate the power of 2, and the remaining 32
  55. bits are used for the significand (or mantissa, as we used to say), without
  56. suppressing the leading bit.  The binary point is at the left of the leading
  57. significant bit.  This representation gives about 9 1/2 decimal digits of
  58. accuracy (somewhat greater than the IEEE single precision representation)
  59. and a range of 10^-4000 to 10^4000, roughly.  That is far more than anyone
  60. needs, but greatly reduces the need to test for overflow and underflow.
  61. I chose to consider any unnormalized number (with the most significant bit
  62. cleared) as zero.  Finally, I set the bias on the exponent or power of 2 to
  63. 3FFF (Hex representation, obviously).  I am not completely satisfied with the
  64. last two decisions (representation of zero, and exponent bias), but I don't
  65. have the energy to re-do all of the work just to see if the decision should
  66. be altered.
  67.  
  68.         The current version uses a separate floating point stack.  In my
  69. initial version, I used the standard parameter stack to hold working floating
  70. point numbers.  This is allowed in the Forth Vendors Group Standard Floating
  71. Point Extension.  As of this writing, it appears that the ANSI Standard Forth
  72. will specify a separate Floating Point stack, so I re-wrote the routines.
  73.  
  74.  
  75. ACCURACY:
  76.  
  77.         In the IEEE Floating Point Standard, the basic four functions (add,
  78. subtract, multiply, and divide) and square root are required to be exact
  79. within one-half of the least significant bit.  It is my intention to meet
  80. that requirement.  To understand how that is done, the reader should consult
  81. some of the original papers.  See, for example, "The INTEL Standard for
  82. Floating Point," by John F. Palmer, Proceedings Compsac77, IEEE Catalog No.
  83. 77CH1291-4C.  The basic idea is that all bits below the least significant
  84. bits are to be considered.  If these bits represent a number greater than
  85. one-half of the least significant bit, then the result is incremented by 1.
  86. If the bits represent a number less than one-half, the result is unchanged.
  87. If the bits represent exactly one-half, then the least significant bit is
  88. examined.  If the least significant bit is 0, the result is unchanged.
  89. Otherwise the result is incremented by 1.  This process is called "rounding
  90. to even."  The trickiest part of this process occurs when subtracting two
  91. numbers which have the same sign (or adding two numbers of opposite sign).
  92. I know the casual reader of these notes is going to think that going to all
  93. of this effort is gross overkill, but the mathematical literature has
  94. numerous cases where this is important.  In any case, if you are going off on
  95. your own to do floating point arithmetic, please do not simply truncate the
  96. results unless you have very good reasons for doing so.  Some of the very
  97. worst examples come from computers designed by Seymore Cray, whose philosophy
  98. seems to be that the results don't have to be computed accurately as long as
  99. they can be computed rapidly.
  100.  
  101.  
  102. DIVISION
  103.  
  104.         If you are interested in details, the floating point division routine
  105. in this package is fairly interesting.  It is my second version.  The initial
  106. version used a more or less standard technique for dividing by successive
  107. subtraction and shifting.  I stumbled on an interesting variation for Knuth's
  108. method for divsion in the documentation of the Forth-based language called
  109. "Abundance" by Roedy Green.  In that package, he performs a double precision
  110. divide by first shifting the divisor left until the most significant bit is
  111. set, obtaining an unsigned quotient and remainder, then adjusting the result
  112. based on the initial amount of shifting.  In a floating point routine, the
  113. operands are almost always normalized, so that the shifting is unnecessary,
  114. except possibly for an initial right shift of the numerator by one place.
  115.  
  116.         The method is somewhat similiar to the technique that you learned in
  117. grammar school, except that instead of working in base ten we use base "s",
  118. where  s = 2^16 .  In other words, each 16 bit word is considered as a digit
  119. in this new base.  We desire a 2 digit quotient and a two digit remainder.
  120. We will first guess the leading digit of the quotient, then determine its
  121. exact value, along with an appropriate remainder.  Then we will determine the
  122. second digit and final remainder the same way.  It turns out that our initial
  123. quess for each quotient digit must be no more than 2 greater than the correct
  124. digit.  At each stage the numerator consists of 3 digits, and the denominator
  125. consists of 2 digits.  We may define the first quotient digit and remainder
  126. as follows:
  127.  
  128.                 (s^2)*A + s*B + C            r1
  129.                 -----------------  =  q1 + ------
  130.                      sD + E                sD + E
  131.  
  132. The 3 terms in the numerator account for a possible right shift in the case
  133. that the initial numerator was greater than or equal to the denominator.  We
  134. thus can guarantee that  A <= D, and  q1 < s.  We also require that the
  135. remainder  r1 < (sD + E).  With a small amount of algebraic manipulation we
  136. can see that
  137.  
  138.                 r1 = (s^2)*A + s*B + C - s*q1*D - q1*E
  139.  
  140. That doesn't appear to be of much help, but just hold on.  We will make our
  141. initial guess for q1 based on a single precision division as follows:
  142.  
  143.                 s*A + B          r0
  144.                 -------  =  g0 + ---
  145.                    D              D
  146.  
  147. The key to simplifying this calculation is that we do not ignore the remainder
  148. in our trial division.  Note that the remainder in this division is
  149.  
  150.                 r0 = s*A + B - g0*D
  151.  
  152. Assume for the time being that  q1 = g0.  It may not be, but our equations
  153. will still be exact!  We can calculate a remainder  r1'  which is
  154.  
  155.                 r1' = s*r0 + C - g0*E
  156.  
  157. by making a simple single precision multiply and a double precision
  158. subtraction.  Our initial guess  g0  may have been too large.  We can tell
  159. that because then  r1' would be negative.  If r1' is >= 0, our initial guess
  160. is the correct guess.
  161.  
  162.         If r1' is negative, we may correct for that by simultaneously adding
  163. the denominator (sD + E) to r1' and decrementing g0 by 1.  We check again to
  164. see if the new remainder r1'' is negative.  If r1'' is also negative, make a
  165. final correction by adding the denominator to r1'' and decrementing g0 again.
  166. This final quotient is correct, and the remainder is also correct.
  167.  
  168.         The process is then repeated for the second quotient digit, by using
  169. the first remainder as the new numerator and then estimating the quotient and
  170. remainder as above.  The result is that we can calculate the double precision
  171. quotient by using 2 single precision divides and two single precision
  172. multiplies, and some trivial additions and subtractions.
  173.  
  174.          The final part of this process is to round the quotient precisely.
  175. This is done by comparing twice the remainder with the divisor.  If 2*rem is
  176. less than the divisor, the quotient is correct.  If 2*rem is greater than the
  177. divisor, the quotient must be incremented by 1.  It 2*rem is exactly equal to
  178. the divisor, we round to even.  That is, if the least significant bit in the
  179. quotient is 1, then add 1 to the quotient.  Otherwise, do not add anything to
  180. the quotient.
  181.  
  182.         There is an additional detail in the above: it is possible that
  183. A = D , even though (sA + B ) < (sD + E) .  If we were to just divide, we
  184. would encounter an overflow situation.  Therefore, consider that the trial
  185. quotient  g0 = s - 1 .  The zero-th remainder is  r0 = sA + B - (sD + D) ,
  186. and the first remainder is
  187.  
  188.         r1 = (s^2)*A + s*B + C - (s^2)*D + s*(D - E) + E
  189.            = s*(B + D - E) + (C + E)
  190.  
  191. Again, the initial guess may be too large, which may be discovered by checking
  192. if r1 is negative.  If it is, then one or two corrections may need to be made,
  193. as in the previous discussion.
  194.  
  195.  
  196. LOGARITHM
  197.  
  198.         These notes are for those few souls who are interested in the details
  199. of implementation of the FLN function for software floating point.  As noted
  200. elsewhere, the basic format for floating point is a 16 bit sign and biased
  201. exponent, followed by a 32 bit normalized fraction.  The biased exponent
  202. occupies bits 0 through 14 with a bias of 3FFF (hexadecimal), and the sign
  203. bit is bit 15 of the first 16 bits.  The binary point of the fraction is just
  204. to the left of the 32 bit fractional part (sometimes called the mantissa).
  205. If the most significant bit of the mantissa is zero, the whole number is
  206. considered zero.
  207.  
  208.         If the argument to the FLN function is negative or zero, it is an
  209. error condition.  After testing for those conditions, we then break down the
  210. problem into two parts, depending on the magnitude of the fraction.  If the
  211. fraction is greater than or equal to 0.78125 (and, by definition, it must be
  212. less than 1.0), then we note that
  213.  
  214.                 ln(X) = ln((2^I) * 0.f) = I*ln(2) + ln(0.f)
  215.  
  216. where I is the unbiased exponent and 0.f is the fractional part.  Calculation
  217. of  ln(0.f) is based on the approximation:
  218.  
  219.         ln(1-x) = -(x + (x^2)/2 + (x^3)/3 + (x^4)/4 + . . . )
  220.  
  221. It may be noted in passing that there are better approximations than the
  222. above, but this one is selected to allow a crucial division to be made using
  223. single precision divisors.  It is necessary to reduce the range of  x  in the
  224. above equation.  (If we did not, it would be necessary to use about 24 terms
  225. in the series when x = 0.25).  This is accomplished by dividing 0.f into
  226. intervals of 2^-5 or 1/32 .  The procedure is as follows:
  227.  
  228.         0.f = 1 - f1 , where  f1 = 1 - 0.f
  229.  
  230.         f1 * 2^5 = J + f2 * 2^5
  231.  
  232.         0.f = 1 - f1 = 1 - (2^-5) * J - f2
  233.  
  234.             = (1 - J*(2^-5)) * [1 - f2/(1 - J*(2^-5))]
  235.  
  236. where  J  is an integer.  The calculation can proceed by using a table of
  237. values for the logarithm of  (1 - J*(2^-5)), and an approximation for
  238. ln(1-x), where
  239.  
  240.         x = f2/(1 - J*(2^-5))
  241.  
  242. In this case, the maximum value for x is 0.0435 .  The series approximation
  243. for 33 bits of accuracy can terminate at the term (x^7)/7 .  In order to
  244. maximize the accuracy of the calculation, we proceed using  y = x*2^4 .
  245. The maximum value of y is 0.696 .  We then proceed to calculate:
  246.  
  247.  
  248.         (2^4)*(-ln(1-x)) = y + (y^2)*(2^-5) + (2/3)*(y^3)*(2^-9) +
  249.                 + (y^4)*(2^-14) + (4/5)*(y^5)*(2^-18) +
  250.                 + (4/6)*(y^6)*(2^-22) + (8/7)*(y^7)*(2^-27)
  251.  
  252. The last term is kept only for rounding purposes.  The factor (8/7) may be
  253. approximated by 1.  It is necessary to use double precision in only the
  254. first 4 terms (well, maybe only the first 3, with extreme care).  The above
  255. equation should be factored, and the calculations made from right to left so
  256. that the smallest terms are calculated first.  (Writing equations in ASCII
  257. is tedious, and reading them is probably hellish, at best).  The calculation
  258. is roughly as follows:
  259.  
  260.         -(2^4)*ln(1-x) = (((((y*(2^-5) + (2/3))y*(2^-4) +
  261.                 +(4/5))*(y^2)*(2^-4) + Y)*(2^-5) + (2/3))*(Y^3)*(2^-4) +
  262.                 +(Y^2))*(2^-5) + Y
  263.  
  264. where  Y  indicates the double precision value and  y  the single precision
  265. value.
  266.  
  267.         The case of the initial fraction 0.f being less than 0.78125 proceeds
  268. along somewhat similiar lines to the above.  We first note that the desired
  269. result may be expressed as
  270.  
  271.         ln(X) = ln(2^(I-1)*(2*0.f)) = (I-1)*ln(2) + ln(2*0.f)
  272.  
  273. where 1.0 <= (2.0*f) < 1.5625 .  In a manner similiar to the first case we
  274. divide the interval up into 19 subintervals of 2^-5 or 1/32 each.  The
  275. proceedure is as follows:
  276.  
  277.         2*0.f = 1 + f1 , where f1 = (2*0.f) - 1
  278.  
  279.         f1 * (2^5) = J + f2 * (2^5)
  280.  
  281.         2*0.f = (1 + J*(2^-5)) * [1 + f2/(1 + J*(2^-5))]
  282.  
  283. where J is an integer.  The calculation can proceed by using a table of
  284. values for the logarithm of (1 + J*(2^-5)) , and an approximation for
  285. ln(1+x), where
  286.  
  287.         x = f2/(1 + J*(2^-5))
  288.  
  289. In this case, the maximum value for x is 0.03125 .  The approximation
  290. proceeds much as in the first case.  The approximation used is:
  291.  
  292.        ln(1+x) = x - (x^2)/2 + (x^3)/3 - (x^4)/4 + (x^5)/5 - (x^6)/6 + (x^7)/8
  293.  
  294. Let x = (2^-5)Y .  The calculation proceeds along the lines indicated by:
  295.  
  296.         (2^5)*ln(1+x) = Y - (2^-6)(Y + (2^-5)(Y^3)(2/3 - (2^-6)(y - (2^-5)*
  297.                         (y^2)(4/5 - (2^-5)y(2/3 - y*(2^-6))))
  298.  
  299.  
  300.  
  301.  
  302.