home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 17 / CD_ASCQ_17_101194.iso / vrac / ajcbcd.zip / AJCBCD.PAS < prev    next >
Pascal/Delphi Source File  |  1994-08-06  |  26KB  |  970 lines

  1. {************************************************}
  2. {                                                }
  3. {   AJC Binary Coded Decimal Unit                }
  4. {                                                }
  5. {   Author:  Andrew J. Cook                      }
  6. {            Omaha, NE                           }
  7. {            CompuServe ID:  71331,501           }
  8. {                                                }
  9. {   Written: May 1994                            }
  10. {                                                }
  11. {   Copyright:  None!  I hereby commit this unit }
  12. {                      to the public domain.     }
  13. {                                                }
  14. {************************************************}
  15.  
  16. unit AJCBCD;
  17.  
  18. interface
  19.  
  20. uses Objects, Strings;
  21.  
  22. const
  23.   DigitSize = SizeOf(Byte);
  24.   bpw_Fixed = 0;
  25.   bpw_Variable = 1;
  26.   bpz_Blank = True;
  27.   bpz_NotBlank = False;
  28.   MaxBCDSize = 100;
  29.   st_Blanks25 = '                         ';
  30.   st_Blanks = st_Blanks25
  31.             + st_Blanks25
  32.             + st_Blanks25
  33.             + st_Blanks25
  34.             + st_Blanks25
  35.             + st_Blanks25
  36.             + st_Blanks25
  37.             + st_Blanks25
  38.             + st_Blanks25
  39.             + st_Blanks25
  40.             + st_Blanks25;
  41.  
  42. type
  43.   PBCDArray = ^TBCDArray;
  44.   TBCDArray = array[1..MaxBCDSize] of byte;
  45.  
  46.   TBCDSign = (BCDNegative, BCDPositive);
  47.  
  48.   PBCD = ^TBCD;
  49.   TBCD = object(TObject)
  50.     BCDSize:  Integer;
  51.     Sign:  TBCDSign;
  52.     Value:  PBCDArray;
  53.     Precision: Byte;
  54.     constructor InitBCD(AVal: PBCD);
  55.     constructor InitReal(AVal: Real; APrec: Byte; ASize: Integer);
  56.     constructor InitPChar(AVal: PChar; APrec: Byte; ASize: Integer);
  57.     destructor Done; virtual;
  58.     constructor Load(var S: TStream);
  59.     procedure Store(var S: TStream);
  60.     function GetValue: PBCDArray;
  61.     function GetSign: TBCDSign;
  62.     function GetPrecision: Byte;
  63.     function GetBCDSize: Integer;
  64.     procedure SetValueBCD(AVal: PBCD);
  65.     procedure SetValueReal(AVal: Real);
  66.     procedure SetValuePChar(AVal: PChar);
  67.     procedure SetSign(ASign: TBCDSign);
  68.     procedure SetPrecision(APrec: Byte);
  69.     procedure SetBCDSize(ASize: Integer);
  70.     procedure AddBCD(AVal: PBCD);
  71.     procedure AddReal(AVal: Real);
  72.     procedure AddPChar(AVal: PChar);
  73.     procedure SubtractBCD(AVal: PBCD);
  74.     procedure SubtractReal(AVal: Real);
  75.     procedure SubtractPChar(AVal: PChar);
  76.     procedure MultiplyByBCD(AVal: PBCD);
  77.     procedure MultiplyByReal(AVal: Real; APrec: Byte);
  78.     procedure MultiplyByPChar(AVal: PChar; APrec: Byte);
  79.     procedure DivideByBCD(AVal: PBCD);
  80.     procedure DivideByReal(AVal: Real; APrec: Byte);
  81.     procedure DivideByPChar(AVal: PChar; APrec: Byte);
  82.     procedure AbsoluteValue;
  83.     procedure Increment;
  84.     procedure Decrement;
  85.     procedure ShiftLeft(ShiftAmount: Byte);
  86.     procedure ShiftRight(ShiftAmount: Byte);
  87.     function BCD2Int: LongInt;
  88.     function BCD2Real: Real;
  89.     function PicStr(picture: string;
  90.                     Width: Integer; BlankWhenZero: Boolean): String;
  91.     function StrPic(dest: PChar; picture: string;
  92.                     Width: Integer; BlankWhenZero: Boolean;
  93.                     Size: Integer): PChar;
  94.     function CompareBCD(AVal: PBCD): Integer;
  95.     function CompareReal(AVal: Real): Integer;
  96.     function ComparePChar(AVal: PChar): Integer;
  97.   end;
  98.  
  99. const
  100.  
  101.   RBCD:  TStreamRec = (ObjType:  60000;
  102.                        VmtLink:  Ofs(TypeOf(TBCD)^);
  103.                        Load:     @TBCD.Load;
  104.                        Store:    @TBCD.Store);
  105.  
  106. var
  107.   BCDZero:  PBCD;
  108.  
  109. implementation
  110.  
  111. {*********************************************************************}
  112. {BCDAdd is a subroutine that adds the value in BCD2 to the value in   }
  113. {BCD1.  It is a simple magnitude addition, as if the two numbers have }
  114. {the same sign.  BCDAdd makes the following assumptions:              }
  115. {  1) the calling routine will manage the proper sign of the result   }
  116. {     of the addition.                                                }
  117. {  2) the BCDSize of the two operands are equal                       }
  118. {  3) the Precision of the two operands are equal                     }
  119. {*********************************************************************}
  120. procedure BCDAdd(BCD1, BCD2: PBCD);
  121. var
  122.   i:  integer;
  123.   Carry:  Byte;
  124. begin
  125.   Carry := 0;
  126.   for i := BCD1^.BCDSize downto 1 do
  127.     begin
  128.       BCD1^.Value^[i] := BCD1^.Value^[i] + BCD2^.Value^[i] + Carry;
  129.       if BCD1^.Value^[i] > 9 then
  130.         begin
  131.           dec(BCD1^.Value^[i], 10);
  132.           Carry := 1;
  133.         end
  134.       else
  135.         Carry := 0;
  136.     end;
  137. end;
  138.  
  139. {**********************************************************************}
  140. {BCDSubtraction is a subroutine that subtracts the value in BCD2 from  }
  141. {the value in BCD1.  It is a simple magnitude subtraction, without     }
  142. {regard to the sign of the operands.  BCDSubtract makes the following  }
  143. {assumptions:                                                          }
  144. {  1) the calling routine will manage the proper sign of the result    }
  145. {     of the subtraction.                                              }
  146. {  2) the BCDSize of the two operands are equal                        }
  147. {  3) the Precision of the two operands are equal                      }
  148. {  4) the magnitude of the value in BCD2 is less than or equal to the  }
  149. {     magnitude of the value in BCD1 so that the routine can perform   }
  150. {     a simple byte by byte subtraction                                }
  151. {**********************************************************************}
  152. procedure BCDSubtract(BCD1, BCD2: PBCD);
  153. var
  154.   i:  integer;
  155.   Borrow:  Byte;
  156. begin
  157.   Borrow := 0;
  158.   for i := BCD1^.GetBCDSize downto 1 do
  159.     begin
  160.       BCD1^.Value^[i] := BCD1^.Value^[i] + 10 - BCD2^.Value^[i] - Borrow;
  161.       if BCD1^.Value^[i] >  9 then
  162.         begin
  163.           dec(BCD1^.Value^[i], 10);
  164.           Borrow := 0;
  165.         end
  166.       else
  167.         Borrow := 1;
  168.     end;
  169. end;
  170.  
  171. constructor TBCD.InitBCD(AVal: PBCD);
  172. begin
  173.   inherited Init;
  174.   BCDSize := AVal^.GetBCDSize;
  175.   GetMem(Value, BCDSize*DigitSize);
  176.   Precision := AVal^.GetPrecision;
  177.   SetValueBCD(AVal);
  178. end;
  179.  
  180. constructor TBCD.InitReal(AVal: Real; APrec: Byte; ASize: Integer);
  181. begin
  182.   inherited Init;
  183.   if ASize > MaxBCDSize then
  184.     BCDSize := MaxBCDSize
  185.   else
  186.     BCDSize := ASize;
  187.   GetMem(Value, ASize*DigitSize);
  188.   Precision := APrec;
  189.   SetValueReal(AVal);
  190. end;
  191.  
  192. constructor TBCD.InitPChar(AVal: PChar; APrec: Byte; ASize: Integer);
  193. begin
  194.   inherited Init;
  195.   if ASize > MaxBCDSize then
  196.     BCDSize := MaxBCDSize
  197.   else
  198.     BCDSize := ASize;
  199.   GetMem(Value, ASize*DigitSize);
  200.   Precision := APrec;
  201.   SetValuePChar(AVal);
  202. end;
  203.  
  204. destructor TBCD.Done;
  205. begin
  206.   FreeMem(Value, BCDSize*DigitSize);
  207.   inherited Done;
  208. end;
  209.  
  210. constructor TBCD.Load(var S: TStream);
  211. begin
  212.   S.Read(BCDSize, SizeOf(BCDSize));
  213.   S.Read(Sign, SizeOf(Sign));
  214.   GetMem(Value, BCDSize*DigitSize);
  215.   S.Read(Value^, BCDSize*DigitSize);
  216.   S.Read(Precision, SizeOf(Precision));
  217. end;
  218.  
  219. procedure TBCD.Store(var S: TStream);
  220. begin
  221.   S.Write(BCDSize, SizeOf(BCDSize));
  222.   S.Write(Sign, SizeOf(Sign));
  223.   S.Write(Value^, BCDSize*DigitSize);
  224.   S.Write(Precision, SizeOf(Precision));
  225. end;
  226.  
  227. function TBCD.GetValue: PBCDArray;
  228. var
  229.   WrkValue:  PBCDArray;
  230. begin
  231.   GetMem(WrkValue, BCDSize*DigitSize);
  232.   Move(Value^, WrkValue^, BCDSize*DigitSize);
  233.   GetValue := WrkValue;
  234. end;
  235.  
  236. function TBCD.GetSign: TBCDSign;
  237. begin
  238.   GetSign := Sign;
  239. end;
  240.  
  241. function TBCD.GetPrecision: Byte;
  242. begin
  243.   GetPrecision := Precision;
  244. end;
  245.  
  246. function TBCD.GetBCDSize:  Integer;
  247. begin
  248.   GetBCDSize := BCDSize;
  249. end;
  250.  
  251. procedure TBCD.SetValueBCD(AVal: PBCD);
  252. var
  253.   SaveSize:  Integer;
  254.   SavePrecision:  Byte;
  255. begin
  256.   if AVal = nil then exit;
  257.  
  258.   FreeMem(Value, BCDSize*DigitSize);
  259.  
  260.   SaveSize := GetBCDSize;
  261.   SavePrecision := GetPrecision;
  262.  
  263.   Value := AVal^.GetValue;
  264.   BCDSize := AVal^.GetBCDSize;
  265.   Precision := AVal^.GetPrecision;
  266.  
  267.   if Precision > SavePrecision then
  268.     begin
  269.       SetBCDSize(SaveSize);
  270.       SetPrecision(SavePrecision);
  271.     end
  272.   else
  273.     begin
  274.       SetPrecision(SavePrecision);
  275.       SetBCDSize(SaveSize);
  276.     end;
  277.  
  278.     SetSign(AVal^.GetSign);
  279. end;
  280.  
  281. procedure TBCD.SetSign(ASign: TBCDSign);
  282. var
  283.   i:  integer;
  284. begin
  285.   Sign := BCDPositive;
  286.   if ASign = BCDPositive then exit;
  287.  
  288.   {allow negative sign only if value is non-zero}
  289.   for i := GetBCDSize downto 1 do
  290.     if Value^[i] <> 0 then
  291.       begin
  292.         Sign := BCDNegative;
  293.         exit;
  294.       end;
  295. end;
  296.  
  297. procedure TBCD.SetValueReal(AVal: Real);
  298. var
  299.   i, BCDIndex:  integer;
  300.   ValStr: String;
  301. begin
  302.   FillChar(Value^, BCDSize*DigitSize, #0);
  303.  
  304.   Str(abs(AVal):BCDSize:Precision, ValStr);
  305.   BCDIndex := BCDSize;
  306.   for i :=length(ValStr) downto 1 do
  307.     if ValStr[i] in ['0'..'9'] then
  308.       begin
  309.         Value^[BCDIndex] := ord(ValStr[i]) - ord('0');
  310.         dec(BCDIndex);
  311.       end;
  312.  
  313.   if AVal < 0.0 then
  314.     SetSign(BCDNegative)
  315.   else
  316.     SetSign(BCDPositive);
  317. end;
  318.  
  319. procedure TBCD.SetValuePChar(AVal: PChar);
  320. var
  321.   i, BCDIndex:  integer;
  322.   SavePrec: Byte;
  323.   SaveSign: TBCDSign;
  324. begin
  325.   if AVal = nil then exit;
  326.  
  327.   SaveSign := BCDPositive;
  328.   SavePrec := Precision;
  329.   Precision := 0;
  330.  
  331.   FillChar(Value^, BCDSize*DigitSize, #0);
  332.  
  333.   if StrLen(AVal) = 0 then
  334.     begin
  335.       Precision := SavePrec;
  336.       SetSign(BCDPositive);
  337.       exit;
  338.     end;
  339.  
  340.   BCDIndex := BCDSize;
  341.   for i := StrLen(AVal) downto 0 do
  342.     case AVal[i] of
  343.       '0'..'9':     begin
  344.                       Value^[BCDIndex] := ord(AVal[i]) - ord('0');
  345.                       dec(BCDIndex);
  346.                     end;
  347.       '(',')','-':  begin
  348.                       SaveSign := BCDNegative;
  349.                     end;
  350.       '.':          begin
  351.                       Precision := BCDSize - BCDIndex;
  352.                     end;
  353.     end;  {case}
  354.  
  355.   SetPrecision(SavePrec);
  356.   SetSign(SaveSign);
  357. end;
  358.  
  359. procedure TBCD.SetPrecision(APrec: Byte);
  360. begin
  361.   if APrec = Precision then exit;
  362.   if APrec < Precision then
  363.     ShiftRight(Precision - APrec)
  364.   else
  365.     ShiftLeft(APrec - Precision);
  366.   Precision := APrec;
  367. end;
  368.  
  369. procedure TBCD.SetBCDSize(ASize: Integer);
  370. var
  371.   SaveSize:  Integer;
  372.   WrkVal:  PBCDArray;
  373. begin
  374.   if ASize = GetBCDSize then exit;
  375.  
  376.   if ASize > MaxBCDSize then ASize := MaxBCDSize;
  377.  
  378.   GetMem(WrkVal, ASize*DigitSize);
  379.   FillChar(WrkVal^, ASize*DigitSize, #0);
  380.  
  381.   if ASize < GetBCDSize then
  382.     Move(Value^[(GetBCDSize-ASize)+1], WrkVal^, ASize*DigitSize)
  383.   else if ASize > GetBCDSize then
  384.     Move(Value^, WrkVal^[(ASize-GetBCDSize)+1], GetBCDSize);
  385.  
  386.   FreeMem(Value, GetBCDSize*DigitSize);
  387.   Value := WrkVal;
  388.   BCDSize := ASize;
  389. end;
  390.  
  391. procedure TBCD.AddBCD(AVal: PBCD);
  392. var
  393.   WrkValue:  PBCD;
  394. begin
  395.   WrkValue := new(PBCD, InitBCD(AVal));
  396.   WrkValue^.SetPrecision(Precision);
  397.   WrkValue^.SetBCDSize(BCDSize);
  398.   if GetSign <> AVal^.GetSign then
  399.     if AVal^.GetSign = BCDNegative then
  400.       begin
  401.         WrkValue^.AbsoluteValue;
  402.         BCDSubtract(@Self, WrkValue);
  403.         Dispose(WrkValue, Done);
  404.         exit;
  405.       end
  406.     else
  407.       {AVal^.GetSign = BCDPositive}
  408.       begin
  409.         AbsoluteValue;
  410.         BCDSubtract(WrkValue, @Self);
  411.         SetValueBCD(WrkValue);
  412.         Dispose(WrkValue, Done);
  413.         exit;
  414.       end;
  415.  
  416.   BCDAdd(@Self, WrkValue);
  417.   Dispose(WrkValue, Done);
  418. end;
  419.  
  420. procedure TBCD.AddReal(AVal: Real);
  421. var
  422.   WrkValue: PBCD;
  423. begin
  424.   WrkValue := new(PBCD, InitReal(AVal, GetPrecision, GetBCDSize));
  425.   AddBCD(WrkValue);
  426.   Dispose(WrkValue, Done);
  427. end;
  428.  
  429. procedure TBCD.AddPChar(AVal: PChar);
  430. var
  431.   WrkValue: PBCD;
  432. begin
  433.   WrkValue := new(PBCD, InitPChar(AVal, GetPrecision, GetBCDSize));
  434.   AddBCD(WrkValue);
  435.   Dispose(WrkValue, Done);
  436. end;
  437.  
  438. procedure TBCD.SubtractBCD(AVal: PBCD);
  439. var
  440.   WrkValue:  PBCD;
  441.   SaveSign:  TBCDSign;
  442. begin
  443.   if AVal = nil then exit;
  444.  
  445.   WrkValue := new(PBCD, InitBCD(AVal));
  446.   WrkValue^.SetPrecision(GetPrecision);
  447.   WrkValue^.SetBCDSize(GetBCDSize);
  448.   if GetSign <> AVal^.GetSign then
  449.     begin
  450.       WrkValue^.SetSign(Sign);
  451.       BCDAdd(@Self, WrkValue);
  452.       Dispose(WrkValue, Done);
  453.       exit;
  454.     end;
  455.  
  456.   SaveSign := Sign;
  457.   AbsoluteValue;
  458.   WrkValue^.AbsoluteValue;
  459.   if CompareBCD(WrkValue) < 0 then
  460.     begin
  461.       BCDSubtract(WrkValue, @Self);
  462.       SetValueBCD(WrkValue);
  463.       if SaveSign = BCDNegative then
  464.         SetSign(BCDPositive)
  465.       else
  466.         SetSign(BCDNegative);
  467.     end
  468.   else
  469.     begin
  470.       BCDSubtract(@Self, WrkValue);
  471.       SetSign(SaveSign);
  472.     end;
  473.  
  474.   Dispose(WrkValue, Done);
  475. end;
  476.  
  477. procedure TBCD.SubtractReal(AVal: Real);
  478. var
  479.   WrkValue: PBCD;
  480. begin
  481.   WrkValue := new(PBCD, InitReal(AVal, GetPrecision, GetBCDSize));
  482.   SubtractBCD(WrkValue);
  483.   Dispose(WrkValue, Done);
  484. end;
  485.  
  486. procedure TBCD.SubtractPChar(AVal: PChar);
  487. var
  488.   WrkValue: PBCD;
  489. begin
  490.   WrkValue := new(PBCD, InitPChar(AVal, GetPrecision, GetBCDSize));
  491.   SubtractBCD(WrkValue);
  492.   Dispose(WrkValue, Done);
  493. end;
  494.  
  495. procedure TBCD.MultiplyByBCD(AVal: PBCD);
  496. var
  497.   NewSign:  TBCDSign;
  498.   WrkValue:  PBCD;
  499.   HighDigit, i, j:  integer;
  500.   SavePrec:  Byte;
  501. begin
  502.   if AVal = nil then exit;
  503.  
  504.   if GetSign = AVal^.GetSign then
  505.     NewSign := BCDPositive
  506.   else
  507.     NewSign := BCDNegative;
  508.   AbsoluteValue;
  509.  
  510.   SavePrec := Precision;
  511.   WrkValue := new(PBCD, InitReal(0, 0, GetBCDSize + AVal^.GetBCDSize));
  512.   Precision := 0;
  513.   i := 1;
  514.   while (i < AVal^.GetBCDSize) and (AVal^.Value^[i] = 0) do
  515.     inc(i);
  516.   HighDigit := i;
  517.  
  518.   for i := AVal^.GetBCDSize downto HighDigit do
  519.     begin
  520.       if AVal^.Value^[i] <> 0 then
  521.         for j := 1 to AVal^.Value^[i] do
  522.           WrkValue^.AddBCD(@Self);
  523.       ShiftLeft(1);
  524.     end;
  525.  
  526.   WrkValue^.Precision := SavePrec + AVal^.GetPrecision;
  527.   WrkValue^.SetPrecision(SavePrec);
  528.   Precision := SavePrec;
  529.   SetValueBCD(WrkValue);
  530.   SetSign(NewSign);
  531. end;
  532.  
  533. procedure TBCD.MultiplyByReal(AVal: Real; APrec: Byte);
  534. var
  535.   WrkVal:  PBCD;
  536. begin
  537.   WrkVal := new(PBCD, InitReal(AVal, APrec, GetBCDSize));
  538.   MultiplyByBCD(WrkVal);
  539.   Dispose(WrkVal, Done);
  540. end;
  541.  
  542. procedure TBCD.MultiplyByPChar(AVal: PChar; APrec: Byte);
  543. var
  544.   WrkVal:  PBCD;
  545. begin
  546.   WrkVal := new(PBCD, InitPChar(AVal, APrec, GetBCDSize));
  547.   MultiplyByBCD(WrkVal);
  548.   Dispose(WrkVal, Done);
  549. end;
  550.  
  551. procedure TBCD.DivideByBCD(AVal: PBCD);
  552. var
  553.   NewSign:  TBCDSign;
  554.   WrkVal, WrkDiv, WrkQuo:  PBCD;
  555.   HighDigit, i, j, IterationCount:  integer;
  556.   TempPrec, QuotientPrec:  Byte;
  557. begin
  558.   if AVal = nil then exit;
  559.  
  560.   if AVal^.CompareReal(0.0) = 0 then exit;  {avoid zero divide}
  561.  
  562.   if GetSign = AVal^.GetSign then
  563.     NewSign := BCDPositive
  564.   else
  565.     NewSign := BCDNegative;
  566.  
  567.   WrkVal := new(PBCD, InitBCD(@Self));
  568.   WrkVal^.AbsoluteValue;
  569.  
  570.   WrkQuo := new(PBCD, InitReal(0, 0, GetBCDSize));
  571.  
  572.   i := 1;
  573.   while (i < WrkVal^.GetBCDSize) and (WrkVal^.Value^[i] = 0) do
  574.     inc(i);
  575.   HighDigit := i;
  576.   WrkVal^.SetPrecision(WrkVal^.GetPrecision+(HighDigit-1));
  577.   TempPrec := WrkVal^.GetPrecision;
  578.   WrkVal^.Precision := 0;
  579.  
  580.   WrkDiv := new(PBCD, InitBCD(AVal));
  581.   WrkDiv^.AbsoluteValue;
  582.   i := 1;
  583.   while (i < WrkDiv^.GetBCDSize) and (WrkDiv^.Value^[i] = 0) do
  584.     inc(i);
  585.   HighDigit := i;
  586.   WrkDiv^.ShiftLeft(HighDigit - 1);
  587.   WrkDiv^.Precision := 0;
  588.  
  589.   QuotientPrec := TempPrec - AVal^.GetPrecision;
  590.   IterationCount := WrkVal^.GetBCDSize - QuotientPrec + GetPrecision;
  591.  
  592.   for i := 1 to IterationCount do
  593.     begin
  594.       while CompareBCD(WrkDiv) > 0 do
  595.         begin
  596.           WrkVal^.SubtractBCD(WrkDiv);
  597.           inc(WrkQuo^.Value^[WrkQuo^.GetBCDSize]);
  598.         end;
  599.       WrkDiv^.ShiftRight(1);
  600.       WrkQuo^.ShiftLeft(1);
  601.     end;
  602.  
  603.   WrkQuo^.Precision := QuotientPrec;
  604.   SetValueBCD(WrkQuo);
  605.   SetSign(NewSign);
  606.  
  607.   Dispose(WrkVal, Done);
  608.   Dispose(WrkQuo, Done);
  609.   Dispose(WrkDiv, Done);
  610. end;
  611.  
  612. procedure TBCD.DivideByReal(AVal: Real; APrec: Byte);
  613. var
  614.   WrkVal:  PBCD;
  615. begin
  616.   WrkVal := new(PBCD, InitReal(AVal, APrec, GetBCDSize));
  617.   DivideByBCD(WrkVal);
  618.   Dispose(WrkVal, Done);
  619. end;
  620.  
  621. procedure TBCD.DivideByPChar(AVal: PChar; APrec: Byte);
  622. var
  623.   WrkVal: PBCD;
  624. begin
  625.   WrkVal := new(PBCD, InitPChar(AVal, APrec, GetBCDSize));
  626.   DivideByBCD(WrkVal);
  627.   Dispose(WrkVal, Done);
  628. end;
  629.  
  630. procedure TBCD.AbsoluteValue;
  631. begin
  632.   SetSign(BCDPositive);
  633. end;
  634.  
  635. procedure TBCD.Increment;
  636. begin
  637.   AddReal(1);
  638. end;
  639.  
  640. procedure TBCD.Decrement;
  641. begin
  642.   SubtractReal(1);
  643. end;
  644.  
  645. procedure TBCD.ShiftLeft(ShiftAmount: Byte);
  646. var
  647.   i:  integer;
  648. begin
  649.   if ShiftAmount = 0 then exit;
  650.   for i := 1 to (BCDSize - ShiftAmount) do
  651.     Value^[i] := Value^[i+ShiftAmount];
  652.   for i := ((BCDSize - ShiftAmount) + 1) to BCDSize do
  653.     Value^[i] := 0;
  654. end;
  655.  
  656. procedure TBCD.ShiftRight(ShiftAmount: Byte);
  657. var
  658.   i:  integer;
  659. begin
  660.   if ShiftAmount = 0 then exit;
  661.   for i := BCDSize downto (ShiftAmount + 1) do
  662.     Value^[i] := Value^[i - ShiftAmount];
  663.   for i := ShiftAmount downto 1 do
  664.     Value^[i] := 0;
  665. end;
  666.  
  667. function TBCD.BCD2Int: LongInt;
  668. var
  669.   i:  integer;
  670.   wrkLongInt:  LongInt;
  671. begin
  672.   BCD2Int := 0;
  673.   if Precision = GetBCDSize then exit;
  674.  
  675.   wrkLongInt := 0;
  676.   i := 1;
  677.   repeat
  678.     wrkLongInt := wrkLongInt * 10;
  679.     wrkLongInt := wrkLongInt + Value^[i];
  680.     inc(i);
  681.   until i = (GetBCDSize - GetPrecision);
  682.   if GetSign = BCDNegative then
  683.     BCD2Int := -wrkLongInt
  684.   else
  685.     BCD2Int := wrkLongInt;
  686. end;
  687.  
  688. function TBCD.BCD2Real: Real;
  689. var
  690.   i:  integer;
  691.   wrkIntegerPart, wrkFractionPart:  real;
  692. begin
  693.   BCD2Real := 0.0;
  694.   wrkIntegerPart := 0;
  695.   wrkFractionPart := 0;
  696.  
  697.   if GetPrecision < GetBCDSize then
  698.     begin
  699.       i := 1;
  700.       repeat
  701.         wrkIntegerPart := wrkIntegerPart * 10.0;
  702.         wrkIntegerPart := wrkIntegerPart + Value^[i];
  703.         inc(i);
  704.       until i = (GetBCDSize - GetPrecision + 1);
  705.     end;
  706.  
  707.   if Precision > 0 then
  708.     begin
  709.       i := GetBCDSize;
  710.       repeat
  711.         wrkFractionPart := wrkFractionPart + Value^[i];
  712.         wrkFractionPart := wrkFractionPart / 10.0;
  713.         dec(i);
  714.       until i = (GetBCDSize - GetPrecision);
  715.     end;
  716.  
  717.   if GetSign = BCDNegative then
  718.     BCD2Real := -(wrkIntegerPart + wrkFractionPart)
  719.   else
  720.     BCD2Real := (wrkIntegerPart + wrkFractionPart);
  721. end;
  722.  
  723. function TBCD.PicStr(picture: string;
  724.                      Width: Integer; BlankWhenZero: Boolean): String;
  725.  
  726. var
  727.    integer_str, decimal_str, pic_str, val_str:  string;
  728.    decimal_encountered, significant_digits_encountered:  boolean;
  729.    number_of_digits, number_of_integer_digits, number_of_decimal_digits,
  730.    sub_pic, sub_val, i:  integer;
  731.  
  732. begin    {pic}
  733.   decimal_encountered := false;
  734.   number_of_digits := 0;
  735.   number_of_integer_digits := 0;
  736.   for i := 1 to length(picture) do
  737.     if upcase(picture[i]) in ['$', '-', '9', 'Z'] then
  738.       begin
  739.         inc(number_of_digits);
  740.         if not decimal_encountered then
  741.           inc(number_of_integer_digits);
  742.       end
  743.     else if picture[i] = '.' then
  744.        decimal_encountered := true;
  745.   number_of_decimal_digits := number_of_digits - number_of_integer_digits;
  746.  
  747.   integer_str := '';
  748.   for i := (GetBCDSize - GetPrecision) downto 1 do
  749.     integer_str := char(ord('0')+Value^[i]) + integer_str;
  750.   if length(integer_str) > number_of_integer_digits then
  751.     delete(integer_str, 1, length(integer_str)-number_of_integer_digits)
  752.   else
  753.     while length(integer_str) < number_of_integer_digits do
  754.       integer_str := '0' + integer_str;
  755.  
  756.   decimal_str := '';
  757.   for i := (GetBCDSize - GetPrecision + 1) to GetBCDSize do
  758.     decimal_str := decimal_str + char(ord('0')+Value^[i]);
  759.   if length(decimal_str) > number_of_decimal_digits then
  760.     delete(decimal_str, number_of_decimal_digits+1, 255)
  761.   else
  762.     while length(decimal_str) < number_of_decimal_digits do
  763.       decimal_str := decimal_str + '0';
  764.  
  765.   val_str := integer_str + decimal_str;
  766.  
  767.   pic_str := copy(st_Blanks, 1, length(picture));
  768.  
  769.   significant_digits_encountered := false;
  770.   sub_pic := 1;
  771.   sub_val := 1;
  772.   while sub_pic <= length(picture) do
  773.     begin
  774.       if val_str[sub_val] in ['1'..'9']then
  775.         significant_digits_encountered := true;
  776.       if upcase(picture[sub_pic]) in ['(', ')'] then
  777.         if Sign = BCDNegative then
  778.           begin
  779.             pic_str[sub_pic] := upcase(picture[sub_pic]);
  780.             sub_pic := sub_pic + 1;
  781.           end
  782.         else
  783.           begin
  784.             pic_str[sub_pic] := ' ';
  785.             sub_pic := sub_pic + 1;
  786.           end
  787.       else if upcase(picture[sub_pic]) in ['Z', '$', '-'] then
  788.         begin
  789.           if significant_digits_encountered then
  790.             pic_str[sub_pic] := val_str[sub_val]
  791.           else
  792.             pic_str[sub_pic] := ' ';
  793.           sub_pic := sub_pic + 1;
  794.           sub_val := sub_val + 1;
  795.         end
  796.       else if picture[sub_pic] = '.' then
  797.         begin
  798.           pic_str[sub_pic] := '.';
  799.           sub_pic := sub_pic + 1;
  800.           significant_digits_encountered := true;
  801.         end
  802.       else if picture[sub_pic] = '9' then
  803.         begin
  804.           pic_str[sub_pic] := val_str[sub_val];
  805.           if pic_str[sub_pic] = ' ' then pic_str[sub_pic] := '0';
  806.           sub_pic := sub_pic + 1;
  807.           sub_val := sub_val + 1;
  808.           significant_digits_encountered := true;
  809.         end
  810.       else if picture[sub_pic] = ',' then
  811.         begin
  812.           if pic_str[sub_pic - 1] = ' ' then
  813.             pic_str[sub_pic] := ' '
  814.           else
  815.             pic_str[sub_pic] := ',';
  816.           sub_pic := sub_pic + 1;
  817.         end
  818.       else
  819.         begin
  820.           pic_str[sub_pic] := upcase(picture[sub_pic]);
  821.           sub_pic := sub_pic + 1;
  822.         end;
  823.     end;
  824.  
  825.   if Sign = BCDNegative then
  826.     begin
  827.       sub_pic := 0;
  828.       while (sub_pic < length(picture)) and
  829.             (picture[sub_pic + 1] in ['(', '-', ',']) do
  830.         sub_pic := sub_pic + 1;
  831.       while (sub_pic > 0) and
  832.             (pic_str[sub_pic] <> ' ') do
  833.         sub_pic := sub_pic - 1;
  834.       if (sub_pic > 0) and
  835.          (picture[sub_pic] <> '(') then
  836.         pic_str[sub_pic] := '-';
  837.     end;
  838.  
  839.   sub_pic := 0;
  840.   while (sub_pic < length(picture)) and
  841.         (picture[sub_pic + 1] in ['(', '$', ',']) do
  842.     sub_pic := sub_pic + 1;
  843.  
  844.   while (sub_pic > 0) and
  845.         (pic_str[sub_pic] <> ' ') do
  846.     sub_pic := sub_pic - 1;
  847.  
  848.   if (sub_pic > 0) and
  849.      (picture[sub_pic] <> '(') then
  850.     pic_str[sub_pic] := '$';
  851.  
  852.   if (BlankWhenZero) and (pic_str = BCDZero^.PicStr(picture, bpw_Fixed, false)) then
  853.     pic_str := copy(st_Blanks, 1, length(picture));
  854.  
  855.   if Width = bpw_fixed then
  856.     PicStr := pic_str
  857.   else
  858.     begin
  859.       if pic_str[1] = ' ' then
  860.         begin
  861.           sub_pic := 1;
  862.           while (sub_pic < length(pic_str)) and
  863.                 (pic_str[sub_pic] = ' ') do
  864.             inc(sub_pic);
  865.           if pic_str[sub_pic] <> ' ' then dec(sub_pic);
  866.           delete(pic_str, 1, sub_pic);
  867.         end;
  868.       if pic_str[length(pic_str)] = ' ' then
  869.         begin
  870.           sub_pic := length(pic_str);
  871.           while (sub_pic > 1) and
  872.                 (pic_str[sub_pic] = ' ') do
  873.             dec(sub_pic);
  874.           if pic_str[sub_pic] <> ' ' then inc(sub_pic);
  875.           delete(pic_str, sub_pic, 255);
  876.         end;
  877.       PicStr := pic_str;
  878.     end;
  879. end;
  880.  
  881. function TBCD.StrPic(dest: PChar; picture: string;
  882.                      Width: Integer; BlankWhenZero: Boolean;
  883.                      Size: Integer): PChar;
  884. var
  885.   WrkStr:  array[0..300] of char;
  886. begin
  887.   if dest = nil then
  888.     begin
  889.       StrPic := nil;
  890.       exit;
  891.     end;
  892.  
  893.   StrPCopy(WrkStr, PicStr(picture, Width, BlankWhenZero));
  894.   StrLCopy(dest, WrkStr, Size);
  895.   StrPic := dest;
  896. end;
  897.  
  898. function TBCD.CompareBCD(AVal: PBCD): Integer;
  899. var
  900.   i:  integer;
  901.   BCD1, BCD2: PBCD;
  902. begin
  903.   if AVal = nil then exit;
  904.  
  905.   if GetSign < AVal^.GetSign then
  906.     begin
  907.       CompareBCD := -1;
  908.       exit;
  909.     end
  910.   else if GetSign > AVal^.GetSign then
  911.     begin
  912.       CompareBCD := +1;
  913.       exit;
  914.     end;
  915.  
  916.   BCD1 := new(PBCD, InitBCD(@Self));
  917.   BCD2 := new(PBCD, InitBCD(AVal));
  918.   if GetBCDSize > AVal^.GetBCDSize then
  919.     BCD2^.SetBCDSize(GetBCDSize)
  920.   else
  921.     BCD1^.SetBCDSize(AVal^.GetBCDSize);
  922.  
  923.   CompareBCD := 0;
  924.   for i := 1 to BCD1^.GetBCDSize do
  925.     begin
  926.       if BCD1^.Value^[i] < BCD2^.Value^[i] then
  927.         begin
  928.           if BCD1^.GetSign = BCDNegative then
  929.             CompareBCD := +1
  930.           else
  931.             CompareBCD := -1;
  932.           Dispose(BCD1, Done);
  933.           Dispose(BCD2, Done);
  934.           exit;
  935.         end
  936.       else if BCD1^.Value^[i] > BCD2^.Value^[i] then
  937.         begin
  938.           if BCD1^.GetSign = BCDNegative then
  939.             CompareBCD := -1
  940.           else
  941.             CompareBCD := +1;
  942.           Dispose(BCD1, Done);
  943.           Dispose(BCD2, Done);
  944.           exit;
  945.         end;
  946.     end;
  947. end;
  948.  
  949. function TBCD.CompareReal(AVal: Real): Integer;
  950. var
  951.   WrkVal: PBCD;
  952. begin
  953.   WrkVal := new(PBCD, InitReal(AVal, GetPrecision, GetBCDSize));
  954.   CompareReal := CompareBCD(WrkVal);
  955.   Dispose(WrkVal, Done);
  956. end;
  957.  
  958. function TBCD.ComparePChar(AVal: PChar): Integer;
  959. var
  960.   WrkVal: PBCD;
  961. begin
  962.   WrkVal := new(PBCD, InitPChar(AVal, GetPrecision, GetBCDSize));
  963.   ComparePChar := CompareBCD(WrkVal);
  964.   Dispose(WrkVal, Done);
  965. end;
  966.  
  967. begin
  968.   BCDZero := new(PBCD, InitReal(0.0, 2, 3));
  969.   RegisterType(RBCD);
  970. end.