home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Pascal / Snippets / Fixed Point Conversions / FixedPointConversions.p < prev    next >
Encoding:
Text File  |  1995-11-14  |  11.5 KB  |  325 lines  |  [TEXT/PJMM]

  1. (**********************************************************************************)
  2. (* Unit to convert Fixed-point numbers to double-precision floating-point numbers.*)
  3. (* Also converts single-precision floatin-point numbers to fixed-point numbers.   *)
  4. (* One routine illustrates the breakdown of a floating-point number.              *)
  5. (* Last modified: 1995-11-13                                                      *)
  6. (*                                                                                *)
  7. (* send comments & suggestions to:                                                *)
  8. (* Daniel W. Rickey                                                               *)
  9. (* Department of Medical Physics                                                  *)
  10. (* Manitoba Cancer Treatment and Research Foundation                              *)
  11. (* 100 Olivia Street                                                              *)
  12. (* Winnipeg, Manitoba                                                             *)
  13. (* R3E 0V9 CANADA                                                                 *)
  14. (* daniel@kaon.mctrf.mb.ca  or physics@escape.ca                                  *)
  15. (**********************************************************************************)
  16. (*                                                                                *)
  17. (* These routines are for programmes that use Fixed point calculations. Conversions *)
  18. (* between floating-point and fixed-point numbers have been performed with the    *)
  19. (* toolbox routines:                                                              *)
  20. (*                  Function Fix2X (X: Fixed): Extended;                          *)
  21. (*                  Function X2Fix (X: Extended): Fixed;                          *)
  22. (* However the the type Extended is not supported by the PowerPC.  Thus, there is *)
  23. (* a need for conversion routines between Fixed point and single/double-precision *)
  24. (* numbers.  This unit contains four routines:                                    *)
  25. (*                                                                                *)
  26. (*                  Procedure SplitReal (X: Real);                                *)
  27. (* This routine does not do anything useful; its purpose is to illustrate how the *)
  28. (* bits of a 4-byte floating point number are used to form the number.            *)
  29. (*                                                                                *)
  30. (*                  Function ConvertIntToDouble (X: LongInt): Double;             *)
  31. (* This routine converts a LongInt to a double-precision floating-point value.    *)
  32. (* The algorithm for this is contained in the Inside Mac: PPC Numerics in the     *)
  33. (* assembly languge section, Listing 13-1, page# 13-4                             *)
  34. (*                                                                                *)
  35. (*                  Function ConvertFixedToDouble (X: Fixed): Double;             *)
  36. (* This routine converts a Fixed-point number to a double floating-point value.   *)
  37. (* Note that this routine performs a double precision add or subtraction and thus *)
  38. (* is very slow on 680x0 machines that do not have a 68881/68882 FPU.             *)
  39. (*                                                                                *)
  40. (*                  Function ConvertRealToFixed (X: Real): Fixed;                 *)
  41. (* This routine converts a floating-point number to a fixed-point number.  Note   *)
  42. (* this routine uses a number of if - then statements and is probably not as      *)
  43. (* efficient as it could be.  This routine does not perform any range checking.   *)
  44. (**********************************************************************************)
  45.  
  46. (* Note that these conversions have not been tested on a PowerPC. *)
  47.  
  48. (* Note!!!  Double precision numbers are assumed to be 8 bytes long *)
  49. (* For the Metrowerks compiler, make sure 8-byte Doubles are selected under compiler options *)
  50.  
  51. Unit FixedPointConversions;
  52. Interface
  53.  
  54.     Uses
  55.         FixMath, NoCoprocessorMath;
  56.  
  57.     Function ConvertIntToDouble (X: LongInt): Double;
  58.     Procedure SplitReal (X: Real);
  59.     Function ConvertFixedToDouble (X: Fixed): Double;
  60.     Function ConvertRealToFixed (X: Real): Fixed;
  61.  
  62. Implementation
  63.  
  64. (****  split a real number into its components ****)
  65. (* this routine does not do anything useful; its purpose is to illustrate how *)
  66. (* the bits of a 4-byte floating point number are used to form the number *)
  67.     Procedure SplitReal (X: Real);
  68.         Const
  69.             kSignBit = $80000000;
  70.             kExponentBits = $7F800000;
  71.             kSignificandBits = $007FFFFF;
  72.             k2ToMinus126 = 1.175494351E-38;
  73.  
  74.         Type
  75.             LongPtr = ^LongInt;
  76.  
  77.         Var
  78.             Signed: Real;
  79.             BiasedExponent: LongInt;
  80.             Fraction: Real;
  81.             Value: Real;
  82.  
  83.     Begin
  84.  (* sign of the real number is contained in the top bit *)
  85.     If Boolean(BSR(BAND(LongPtr(@X)^, kSignBit), 31)) Then
  86.         Signed := -1
  87.     Else
  88.         Signed := +1;
  89.  
  90.  {get the biased exponent of the 4-byte real number}
  91.     BiasedExponent := BSR(BAND(LongPtr(@X)^, kExponentBits), 23);
  92.  
  93.  {get fractional part of real number}
  94.     Fraction := BAND(LongPtr(@X)^, kSignificandBits);
  95.     Fraction := Fraction / $800000;
  96.  
  97.  (* decode BiasedExponent & Fraction parts of number to obtain its value *)
  98.  (* values of 0 and 255 have special meanings *)
  99.     If (BiasedExponent = 0) Then
  100.         Begin
  101.         If (Fraction = 0) Then
  102.             Begin
  103.    (* If BiasedExponent = 0 AND Fraction = 0; value is Zero *)
  104.             Value := 0;
  105.             End
  106.         Else
  107.             Begin
  108.    (* If BiasedExponent = 0 AND Fraction ≠ 0; value is denormalised real number *)
  109.    (* sign * 2^-126 * Fraction; The number is VERY small about ±2^(-126) *)
  110.             Value := Signed * k2ToMinus126 * Fraction;
  111.             End;  {If (Fraction = 0)}
  112.         End
  113.     Else If (BiasedExponent = 255) Then
  114.         Begin
  115.         If (Fraction = 0) Then
  116.             Begin
  117.    (* If BiasedExponent = 255 AND Fraction = 0; value is an infinity *)
  118.             Value := Signed * Inf;
  119.             End
  120.         Else
  121.             Begin
  122.    (* BiasedExponent = 255 AND Fraction ≠ 0; value is NaN *)
  123.    (* A NaN code is contained in bits 8 through 15 of the fraction field *)
  124.             Value := X;
  125.             End;
  126.         End
  127.     Else
  128.         Begin
  129.   (* If 0 < BiasedExponent < 255; value is a normalised real number *)
  130.   (* =sign * 2^ (BiasedExponent-127) * (1+Fraction) *)
  131.         Value := Signed * Exp2(BiasedExponent - 127) * (1 + Fraction);
  132.         End;
  133.  
  134.     Writeln('Value: ', Value : 1 : 8);
  135.     End;  {SplitReal}
  136.  
  137.  
  138.  (**** convert a LongInt to a double floating-point value ****)
  139.  (* The algorithm for this is contained in the Inside Mac: PPC Numerics*)
  140.  (* in the assembly languge section, Listing 13-1, page# 13-4 *)
  141.     Function ConvertIntToDouble (X: LongInt): Double;
  142.         Const
  143.             kExponent = $43300000;
  144.             kInteger = $80000000;
  145.  
  146.         Type
  147.             LongPtr = ^LongInt;
  148.  
  149.         Var
  150.             TempReal, Value: Double;
  151.             aLongPtr: LongPtr;
  152.  
  153.     Begin
  154.  {place $43300000 in top 32 bits of double floating point number}
  155.     aLongPtr := @Value;
  156.     aLongPtr^ := kExponent;
  157.  
  158.  {invert sign of integer}
  159.     X := BXOR(X, $80000000);
  160.  
  161.  {place the longInt in bottom 32 bits of double floating-point value}
  162.     aLongPtr := LongPtr(Ord(@Value) + 4);
  163.     aLongPtr^ := X;
  164.  
  165.  {need to subtract real value of $4330000080000000 from value}
  166.  {first place $43300000 in top 32 bits of a temporary double}
  167.     aLongPtr := @TempReal;
  168.     aLongPtr^ := kExponent;
  169.  
  170.  {next place $80000000 in bottom 32 bits of a temporary double}
  171.     aLongPtr := LongPtr(Ord(@TempReal) + 4);
  172.     aLongPtr^ := kInteger;
  173.  
  174.  {subtract double value of $4330000080000000 from value}
  175.     ConvertIntToDouble := Value - TempReal;
  176.     End;  {ConvertIntToDouble}
  177.  
  178.  
  179.  (**** convert a Fixed-point number to a double floating-point value ****)
  180.     Function ConvertFixedToDouble (X: Fixed): Double;
  181.         Const
  182.             kPositiveExponent = $42300000;
  183.             kNegativeExponent = $C2300000;
  184.  
  185.             kFixedOne = $10000;    {Fixed-point value of one}
  186.  
  187.         Type
  188.             LongPtr = ^LongInt;
  189.  
  190.         Var
  191.             TempReal, Value: Double;
  192.             aLongPtr: LongPtr;
  193.  
  194.     Begin
  195.  {need to subtract or add real value of $4230000080000000 from value}
  196.  {first place $42300000 in top 32 bits of a temporary double}
  197.     aLongPtr := @TempReal;
  198.     aLongPtr^ := kPositiveExponent;
  199.  
  200.  {next place $80000000 in bottom 32 bits of a temporary double}
  201.     aLongPtr := LongPtr(Ord(@TempReal) + 4);
  202.     aLongPtr^ := kFixedOne;
  203.  
  204.  {get pointer to top 4 bytes of a double floating point number}
  205.     aLongPtr := @Value;
  206.  
  207.  {have to handle things a bit differently for negative numbers}
  208.  {Negative numbers of type Fixed are the two's complement}
  209.     If X >= 0 Then
  210.         Begin
  211.   {place exponent with sign bit set to 0 in top of double}
  212.         aLongPtr^ := kPositiveExponent;
  213.  
  214.   {place the Fixed number in bottom 32 bits of double floating-point value}
  215.         aLongPtr := LongPtr(Ord(@Value) + 4);
  216.         aLongPtr^ := X + kFixedOne;
  217.  
  218.   {double is encoded as exponent*(1 + BSR(X,16)), therefore need to}
  219.   {subtract double value of $4230000080000000 from value}
  220.   {to give exponent*(1 + BSR(X,16)) - exponent*1 = exponent*BSR(X,16)}
  221.         ConvertFixedToDouble := Value - TempReal;
  222.         End
  223.     Else
  224.         Begin
  225.   {place exponent with sign bit set to 1 in top of double}
  226.         aLongPtr^ := kNegativeExponent;
  227.  
  228.   {place the fixed-point number in bottom 32 bits of double floating-point value}
  229.         aLongPtr := LongPtr(Ord(@Value) + 4);
  230.   
  231.   {because it is negative, we need to convert from two's complement}
  232.   {use the built in bnot or Bitnot routines}
  233.   {$IFC UNDEFINED THINK_Pascal}
  234.   aLongPtr^ := Bnot(X) + 1 + kFixedOne;
  235.   {$ELSEC}
  236.   aLongPtr^ := Bitnot(X) + 1 + kFixedOne;
  237.   {$ENDC}
  238.         
  239.  
  240.   {because value is negative, add double value of $4230000080000000 to value}
  241.   {double is encoded as -exponent*(1 + BSR(X,16)), therefore need to}
  242.   {subtract double value of $4230000080000000 from value}
  243.   {to give -exponent*(1 + BSR(X,16)) + exponent*1 = -exponent*BSR(X,16)}
  244.  
  245.         ConvertFixedToDouble := Value + TempReal;
  246.         End;
  247.     End;  {ConvertFixedToDouble}
  248.  
  249.  
  250. (**** converts a floating-point number to a fixed-point number ****)
  251.     Function ConvertRealToFixed (X: Real): Fixed;
  252.         Const
  253.             kSignBit = $80000000;
  254.             kExponentBits = $7F800000;
  255.             kSignificandBits = $007FFFFF;
  256.             k2ToMinus126 = 1.175494351E-38;
  257.             kMaxFixedPoint = $7FFFFFFF;
  258.             kFixedOne = $00010000;
  259.  
  260.         Type
  261.             LongPtr = ^LongInt;
  262.  
  263.         Var
  264.             theSign: LongInt;
  265.             Exponent: LongInt;
  266.             Fraction: LongInt;
  267.             TempFixed: Fixed;
  268.  
  269.     Begin
  270.  (* sign of real number *)
  271.     If Boolean(BSR(BAND(LongPtr(@X)^, kSignBit), 31)) Then
  272.         theSign := -1
  273.     Else
  274.         theSign := +1;
  275.  
  276.     Exponent := BSR(BAND(LongPtr(@X)^, kExponentBits), 23) - 127;
  277.     Fraction := BAND(LongPtr(@X)^, kSignificandBits);
  278.  
  279.     If (Exponent = -127) Then
  280.         Begin
  281.   (* If Exponent = -127 AND Fraction = 0; floating-point number is Zero *)
  282.   (* Else If Exponent = -127 AND Fraction ≠ 0; loating-point number is *)
  283.   (* denormalised real number and = sign * 2^-126 * Fraction *)
  284.   (* denormalised is too small to represent with fixed-point numbers *)
  285.         TempFixed := 0;
  286.         End
  287.     Else If (Exponent = 128) Then
  288.         Begin
  289.         If (Fraction = 0) Then
  290.             Begin
  291.    (* Exponent = 128 AND Fraction = 0; value is an infinity *)
  292.             TempFixed := theSign * kMaxFixedPoint;
  293.             End
  294.         Else
  295.             Begin
  296.    (* Exponent = 128 AND Fraction ≠ 0; value is NaN *)
  297.             TempFixed := 0;
  298.             End;
  299.         End
  300.     Else
  301.         Begin
  302.   (* -127 < Exponent < 128; value is normalised real number *)
  303.   (* sign * 2^(Exponent) * (1+Fraction) *)
  304.  
  305.   {first calculate 2^(Exponent); use a fixed-point value one as start point}
  306.         If Exponent > 0 Then
  307.             TempFixed := BSL(kFixedOne, Exponent)
  308.         Else
  309.             TempFixed := BSR(kFixedOne, -Exponent);
  310.  
  311.   {now add 2^(Exponent)*Fraction to the fixed-point number}
  312.   {subtract 7 from exponent to place fraction at bit 16 instead of bit 23}
  313.         Exponent := Exponent - 7;
  314.         If Exponent > 0 Then
  315.             TempFixed := TempFixed + BSL(Fraction, Exponent) + BSL(1, Exponent)
  316.         Else
  317.             TempFixed := TempFixed + BSR(Fraction, -Exponent) + BSL(1, Exponent);
  318.  
  319.         End;
  320.  
  321.  {now multiply by sign}
  322.     ConvertRealToFixed := theSign * TempFixed;
  323.     End;  {ConvertRealToFixed}
  324.  
  325. End.