/[projects]/dao/DelphiScanner/Components/tpsystools_4.04/source/StBCD.pas
ViewVC logotype

Contents of /dao/DelphiScanner/Components/tpsystools_4.04/source/StBCD.pas

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2671 - (show annotations) (download)
Tue Aug 25 18:15:15 2015 UTC (8 years, 9 months ago) by torben
File size: 76467 byte(s)
Added tpsystools component
1 // Upgraded to Delphi 2009: Sebastian Zierer
2
3 (* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is TurboPower SysTools
17 *
18 * The Initial Developer of the Original Code is
19 * TurboPower Software
20 *
21 * Portions created by the Initial Developer are Copyright (C) 1996-2002
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * ***** END LICENSE BLOCK ***** *)
27
28 {*********************************************************}
29 {* SysTools: StBCD.pas 4.04 *}
30 {*********************************************************}
31 {* SysTools: BCD arithmetic functions *}
32 {*********************************************************}
33
34 {$I StDefine.inc}
35
36 {Notes:
37 The BCD format matches that defined by Turbo Pascal 3.0. It is as follows:
38
39 LSB MSB (most significant byte at end)
40 |<------ Mantissa ------>|
41 1 2 3 4 5 6 7 8 9 10 <- Byte #
42 sE ML ML ML ML ML ML ML ML ML
43 ^ ^^--- Less significant digit
44 | |---- More significant digit
45 |
46 v
47 7 6 5 4 3 2 1 0 <-- Bit # (in Byte 1)
48 s E E E E E E E
49 ^ <--exponent->
50 | |
51 | |--- exponent has offset of $3F (eg, $41 means 10^2 = 100)
52 |----------- sign bit (0 = positive, 1 = negative)
53
54 Unpacked BCD format
55 -------------------
56 Many of the routines that follow work with these reals in an unpacked
57 format. That is, before an arithmetic operation is performed, the mantissas
58 are expanded (unpacked) so that there is one digit per byte. After unpacking,
59 the reals look like this:
60
61 LSB MSB
62 |<------------------ mantissa --------------------->|
63 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
64 sE 0d 0d 0d 0d 0d 0d 0d 0d 0d 0d 0d 0d 0d 0d 0d 0d 0d 0d 00
65 ^^
66 ||---- Digit
67 |----- 0
68 Byte 1 is unchanged.
69 Bytes 2-19 contain the digits in the mantissa, LSB first. The high
70 nibble of each byte is 0, and the low nibble contains the digit.
71 Byte 20, sometimes used to keep track of overflow, is set to 0.
72
73 The constant BcdSize determines the size and accuracy of the Bcd
74 routines. It can be any value in the range 4-20 bytes. The default
75 value of 10 gives 18 digits of accuracy. A size of 20 gives 38 digits
76 of accuracy.
77
78 The BCD routines are thread-aware; all temporary variables are local.
79
80 STBCD uses the DecimalSeparator global variable from the SYSUTILS unit
81 wherever it needs a decimal point. As such the formatting of BCD
82 strings is aware of international differences.
83
84 The transcendental routines (Sqrt, Ln, Exp, Pow) are accurate for
85 all but 1 or 2 of the available digits of storage. For BcdSize =
86 10, this means 16-17 accurate digits; for BcdSize = 20, this means
87 36-37 accurate digits. The last digit or two is lost to roundoff
88 errors during the calculations.
89
90 Algorithms used for transcendental routines (depending on BcdSize):
91 Sqrt:
92 Herron's iterative approximation
93 Exp:
94 <= 10 bytes, Chebyshev polynomials per Cody and Waite
95 > 10 bytes, traditional series expansion
96 Ln:
97 <= 10 bytes, Chebyshev polynomials of rational approximation
98 per Cody and Waite
99 > 10 bytes, Carlson's iterative approximation
100 Pow:
101 straight multiplication for integer powers
102 use of Exp and Ln for non-integer powers
103
104 Computation of Exp and Ln for BcdSize > 10 bytes is quite slow. Exp
105 takes up to 30 terms to fill in all the digits when BcdSize = 20.
106 Ln takes 9 iterations for BcdSize = 20, but each iteration is complicated
107 and involves a sqrt, a divide, and other simpler operations.
108
109 FormatBcd mimics the FormatFloat routine from the SYSUTILS unit.
110 StrGeneralBcd mimics the FloatToStrF routine with the ffGeneral option.
111 See the documentation for those routines for more information.
112 }
113
114
115 unit StBCD;
116
117 interface
118
119 uses
120 Windows,
121 SysUtils,
122 StConst,
123 StBase,
124 StStrL;
125
126 const
127 BcdSize = 10; {bytes in BCD, valid range 4-20}
128 {.Z+}
129 MantissaDigits = 2*(BcdSize-1); {digits in mantissa}
130 OverflowChar = '*'; {character used to fill an overflow string}
131 {.Z-}
132
133 type
134 TBcd = array[0..BcdSize-1] of Byte;
135
136 var
137 {these values are set up by the initialization block}
138 ZeroBcd : TBcd;
139 MinBcd : TBcd;
140 MaxBcd : TBcd;
141 BadBcd : TBcd;
142 PiBcd : TBcd;
143 eBcd : TBcd;
144 Ln10Bcd : TBcd;
145
146 {$IFNDEF CBuilder}
147 function AddBcd(const B1, B2 : TBcd) : TBcd;
148 {-Return B1+B2}
149 function SubBcd(const B1, B2 : TBcd) : TBcd;
150 {-Return B1-B2}
151 function MulBcd(const B1, B2 : TBcd) : TBcd;
152 {-Return B1*B2}
153 function DivBcd(const B1, B2 : TBcd) : TBcd;
154 {-Return B1/B2}
155 function ModBcd(const B1, B2 : TBcd) : TBcd;
156 {-Return B1 mod B2}
157 function NegBcd(const B : TBcd) : TBcd;
158 {-Return the negative of B}
159 function AbsBcd(const B : TBcd) : TBcd;
160 {-Return the absolute value of B}
161 function FracBcd(const B : TBcd) : TBcd;
162 {-Return the fractional part of B}
163 function IntBcd(const B : TBcd) : TBcd;
164 {-Return the integer part of B, as a BCD real}
165 function RoundDigitsBcd(const B : TBcd; Digits : Cardinal) : TBcd;
166 {-Return B rounded to specified total digits of accuracy}
167 function RoundPlacesBcd(const B : TBcd; Places : Cardinal) : TBcd;
168 {-Return B rounded to specified decimal places of accuracy}
169 function ValBcd(const S : string) : TBcd;
170 {-Convert a string to a BCD}
171 function LongBcd(L : LongInt) : TBcd;
172 {-Convert a long integer to a BCD}
173 function ExtBcd(E : Extended) : TBcd;
174 {-Convert an extended real to a BCD}
175 function ExpBcd(const B : TBcd) : TBcd;
176 {-Return e**B}
177 function LnBcd(const B : TBcd) : TBcd;
178 {-Return natural log of B}
179 function IntPowBcd(const B : TBcd; E : LongInt) : TBcd;
180 {-Return B**E, where E is an integer}
181 function PowBcd(const B, E : TBcd) : TBcd;
182 {-Return B**E}
183 function SqrtBcd(const B : TBcd) : TBcd;
184 {-Return the square root of B}
185 {$ENDIF}
186
187 function CmpBcd(const B1, B2 : TBcd) : Integer;
188 {-Return <0 if B1<B2, =0 if B1=B2, >0 if B1>B2}
189 function EqDigitsBcd(const B1, B2 : TBcd; Digits : Cardinal) : Boolean;
190 {-Return True if B1 and B2 are equal after rounding to specified digits}
191 function EqPlacesBcd(const B1, B2 : TBcd; Digits : Cardinal) : Boolean;
192 {-Return True if B1 and B2 are equal after rounding to specified decimal places}
193 function IsIntBcd(const B : TBcd) : Boolean;
194 {-Return True if B has no fractional part (may still not fit into a LongInt)}
195 function TruncBcd(const B : TBcd) : LongInt;
196 {-Return B after discarding its fractional part}
197 function BcdExt(const B : TBcd) : Extended;
198 {-Convert B to an extended real}
199 function RoundBcd(const B : TBcd) : LongInt;
200 {-Round B rounded to the nearest integer}
201 function StrBcd(const B : TBcd; Width, Places : Cardinal) : string;
202 {-Convert BCD to a string in floating point format}
203 function StrExpBcd(const B : TBcd; Width : Cardinal) : string;
204 {-Convert BCD to a string in scientific format}
205 function FormatBcd(const Format: string; const B : TBcd): string;
206 {-Format a BCD like FormatFloat does for Extended}
207 function StrGeneralBcd(const B : TBcd) : string;
208 {-Format a BCD like FloatToStrF does with ffGeneral format, MantissaDigits
209 for Precision, and zero for Digits}
210 function FloatFormBcd(const Mask : string; B : TBCD;
211 const LtCurr, RtCurr : string;
212 Sep, DecPt : Char) : string;
213 {-Returns a formatted string with digits from B merged into the Mask}
214 procedure ConvertBcd(const SrcB; SrcSize : Byte; var DestB; DestSize : Byte);
215 {-Convert a BCD of one size to another size}
216
217 {the following routines are provided to support C++Builder}
218 {$IFDEF CBuilder}
219 procedure AddBcd_C(const B1, B2 : TBcd; var Res : TBcd);
220 procedure SubBcd_C(const B1, B2 : TBcd; var Res : TBcd);
221 procedure MulBcd_C(const B1, B2 : TBcd; var Res : TBcd);
222 procedure DivBcd_C(const B1, B2 : TBcd; var Res : TBcd);
223 procedure ModBcd_C(const B1, B2 : TBcd; var Res : TBcd);
224 procedure NegBcd_C(const B : TBcd; var Res : TBcd);
225 procedure AbsBcd_C(const B : TBcd; var Res : TBcd);
226 procedure FracBcd_C(const B : TBcd; var Res : TBcd);
227 procedure IntBcd_C(const B : TBcd; var Res : TBcd);
228 procedure RoundDigitsBcd_C(const B : TBcd; Digits : Cardinal; var Res : TBcd);
229 procedure RoundPlacesBcd_C(const B : TBcd; Places : Cardinal; var Res : TBcd);
230 procedure ValBcd_C(const S : string; var Res : TBcd);
231 procedure LongBcd_C(L : LongInt; var Res : TBcd);
232 procedure ExtBcd_C(E : Extended; var Res : TBcd);
233 procedure ExpBcd_C(const B : TBcd; var Res : TBcd);
234 procedure LnBcd_C(const B : TBcd; var Res : TBcd);
235 procedure IntPowBcd_C(const B : TBcd; E : LongInt; var Res : TBcd);
236 procedure PowBcd_C(const B, E : TBcd; var Res : TBcd);
237 procedure SqrtBcd_C(const B : TBcd; var Res : TBcd);
238 {$ENDIF}
239
240 {the following function is interfaced to avoid hints from the compiler}
241 {for its non use when the BcdSize constant is set a value less than 11}
242 {$IFNDEF CBuilder}
243 function LnBcd20(const B : TBcd) : TBcd;
244 {$ENDIF}
245
246 {=========================================================}
247
248 implementation
249
250 {Define to use assembly language in primitive routines below}
251 {$DEFINE UseAsm}
252
253 const
254 NoSignBit = $7F; {mask to get just the exponent}
255 SignBit = $80; {mask to get just the sign}
256 ExpBias = $3F; {bias added to actual exponent value}
257 SigDigits = MantissaDigits+1; {counts overflow digit}
258
259 type
260 TUnpBcd = array[0..SigDigits] of Byte; {unpacked BCD}
261 PUnpBcd = ^TUnpBcd;
262 TIntBcd = array[0..4*BcdSize-1] of Byte; {double size buffer for mult/div}
263
264 {$IFDEF CBuilder}
265 function AddBcd(const B1, B2 : TBcd) : TBcd; forward;
266 function SubBcd(const B1, B2 : TBcd) : TBcd; forward;
267 function MulBcd(const B1, B2 : TBcd) : TBcd; forward;
268 function DivBcd(const B1, B2 : TBcd) : TBcd; forward;
269 function ModBcd(const B1, B2 : TBcd) : TBcd; forward;
270 function NegBcd(const B : TBcd) : TBcd; forward;
271 function AbsBcd(const B : TBcd) : TBcd; forward;
272 function FracBcd(const B : TBcd) : TBcd; forward;
273 function IntBcd(const B : TBcd) : TBcd; forward;
274 function RoundDigitsBcd(const B : TBcd; Digits : Cardinal) : TBcd; forward;
275 function RoundPlacesBcd(const B : TBcd; Places : Cardinal) : TBcd; forward;
276 function ValBcd(const S : string) : TBcd; forward;
277 function LongBcd(L : LongInt) : TBcd; forward;
278 function ExtBcd(E : Extended) : TBcd; forward;
279 function ExpBcd(const B : TBcd) : TBcd; forward;
280 function LnBcd(const B : TBcd) : TBcd; forward;
281 function IntPowBcd(const B : TBcd; E : LongInt) : TBcd; forward;
282 function PowBcd(const B, E : TBcd) : TBcd; forward;
283 function SqrtBcd(const B : TBcd) : TBcd; forward;
284 {$ENDIF}
285
286 function FastValPrep(S : String) : String;
287 var
288 I : LongInt;
289 begin
290 I := Pos('.', S);
291 if I > 0 then
292 S[I] := {$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator;
293 Result := S;
294 end;
295
296 procedure RaiseBcdError(Code : LongInt);
297 var
298 E : EStBCDError;
299 begin
300 E := EStBCDError.CreateResTP(Code, 0);
301 E.ErrorCode := Code;
302 raise E;
303 end;
304
305 procedure AddMantissas(const UB1 : TUnpBcd; var UB2 : TUnpBcd);
306 {$IFDEF UseAsm}
307 asm
308 push esi
309 push edi
310 mov esi,UB1
311 mov edi,UB2
312 {inc esi}
313 {inc edi}
314 mov ecx,SigDigits
315 clc
316 @1: mov al,[esi] {UB1}
317 inc esi
318 adc al,[edi] {UB1+UB2+CF}
319 aaa
320 mov [edi],al {update UB2}
321 inc edi
322 dec ecx
323 jnz @1
324 jnc @2
325 inc byte ptr [edi]
326 @2: pop edi
327 pop esi
328 end;
329 {$ELSE}
330 var
331 I : Integer;
332 T, C : Byte;
333 begin
334 C := 0;
335 for I := 0 to MantissaDigits do begin
336 T := UB2[I]+UB1[I]+C;
337 if T > 9 then begin
338 C := 1;
339 dec(T, 10);
340 end else
341 C := 0;
342 UB2[I] := T;
343 end;
344 UB2[SigDigits] := C;
345 end;
346 {$ENDIF}
347
348 function IsZeroMantissa(const UB : TUnpBcd) : Boolean;
349 {$IFDEF UseAsm}
350 asm
351 push edi
352 mov edi,UB
353 {inc edi}
354 xor al,al
355 mov ecx,SigDigits
356 repe scasb
357 jne @1
358 inc al
359 @1:pop edi
360 end;
361 {$ELSE}
362 var
363 I : Integer;
364 begin
365 for I := 0 to MantissaDigits do
366 if UB[I] <> 0 then begin
367 Result := False;
368 Exit;
369 end;
370 Result := True;
371 end;
372 {$ENDIF}
373
374 procedure NegMantissa(var UB : TUnpBcd);
375 {$IFDEF UseAsm}
376 asm
377 push edi
378 mov edi,UB
379 {inc edi}
380 mov ecx,SigDigits
381 xor dh,dh
382 clc
383 @1: mov al,dh
384 sbb al,[edi]
385 aas
386 mov [edi],al
387 inc edi
388 dec ecx
389 jnz @1
390 pop edi
391 end;
392 {$ELSE}
393 var
394 I : Integer;
395 C : Byte;
396 begin
397 C := 1;
398 for I := 0 to MantissaDigits do begin
399 UB[I] := 9+C-UB[I];
400 if UB[I] > 9 then begin
401 dec(UB[I], 10);
402 C := 1;
403 end else
404 C := 0;
405 end;
406 end;
407 {$ENDIF}
408
409 procedure NormalizeMantissa(var UB : TunpBcd; var E : Integer);
410 var
411 I, Shift : Integer;
412 begin
413 {find most significant non-zero digit}
414 for I := MantissaDigits downto 0 do
415 if UB[I] <> 0 then begin
416 Shift := MantissaDigits-I;
417 if Shift >= E then begin
418 {number disappears}
419 E := 0;
420 FillChar(UB[0], SigDigits, 0);
421 end else if Shift <> 0 then begin
422 dec(E, Shift);
423 move(UB[0], UB[Shift], SigDigits-Shift);
424 FillChar(UB[0], Shift, 0);
425 end;
426 Exit;
427 end;
428 {mantissa is all zeros}
429 E := 0;
430 end;
431
432 procedure SetZero(var B : TBcd);
433 begin
434 FillChar(B, SizeOf(TBcd), 0);
435 end;
436
437 procedure Pack(const UB : TUnpBcd; Exponent : Integer; Sign : Byte;
438 var B : TBcd);
439 {$IFNDEF UseAsm}
440 var
441 I : Integer;
442 {$ENDIF}
443 begin
444 if Exponent <= 0 then
445 SetZero(B)
446
447 else begin
448 B[0] := Sign or Exponent;
449 {repack digits}
450 {$IFDEF UseAsm}
451 asm
452 push esi
453 push edi
454 mov esi,UB
455 mov edi,B
456 inc esi
457 inc edi
458 mov ecx,BcdSize-1
459 @1: mov ax,[esi]
460 inc esi
461 inc esi
462 shl ah,4
463 or al,ah
464 mov [edi],al
465 inc edi
466 dec ecx
467 jnz @1
468 pop edi
469 pop esi
470 end;
471 {$ELSE}
472 for I := 1 to BcdSize-1 do
473 B[I] := UB[2*I-1] or (UB[2*I] shl 4);
474 {overflow digit ignored}
475 {$ENDIF}
476 end;
477 end;
478
479 procedure RoundMantissa(var UB : TUnpBcd; Start : Integer);
480 var
481 {$IFNDEF UseAsm}
482 I : Integer;
483 {$ENDIF}
484 C : Byte;
485 begin
486 if Start > MantissaDigits then begin
487 Start := SigDigits;
488 C := 0;
489 end else
490 C := UB[Start];
491 FillChar(UB[1], Start, 0);
492 if C < 5 then
493 Exit;
494 {$IFDEF UseAsm}
495 asm
496 push edi
497 mov edi,UB
498 mov eax,Start
499 add edi,eax
500 inc edi
501 mov ecx,MantissaDigits
502 sub ecx,eax
503 jle @2
504 stc
505 @1: mov al,[edi]
506 adc al,0
507 aaa
508 mov [edi],al
509 inc edi
510 jnc @3
511 dec ecx
512 jnz @1
513 @2: inc byte ptr [edi]
514 @3: pop edi
515 end;
516 {$ELSE}
517 C := 1;
518 for I := Start+1 to MantissaDigits do begin
519 inc(UB[I], C);
520 if UB[I] > 9 then begin
521 dec(UB[I], 10);
522 C := 1;
523 end else
524 {done rounding}
525 Exit;
526 end;
527 {set overflow digit if we get here}
528 inc(UB[SigDigits]);
529 {$ENDIF}
530 end;
531
532 procedure ShiftMantissaDown(var UB : TUnpBcd; Shift : Integer);
533 begin
534 if Shift > MantissaDigits then
535 {UB disappears when shifted}
536 FillChar(UB[0], SigDigits+1, 0)
537
538 else if Shift > 0 then begin
539 Move(UB[Shift], UB[0], SigDigits+1-Shift);
540 FillChar(UB[SigDigits+1-Shift], Shift, 0);
541 end;
542 end;
543
544 procedure SubMantissas(const UB1 : TUnpBcd; var UB2 : TUnpBcd);
545 {$IFDEF UseAsm}
546 asm
547 push esi
548 push edi
549 mov esi,UB1
550 mov edi,UB2
551 {inc esi}
552 {inc edi}
553 mov ecx,SigDigits
554 clc
555 @1: mov al,[edi] {UB2}
556 sbb al,[esi] {UB2-UB1-CF}
557 aas
558 mov [edi],al {update UB2}
559 inc edi
560 inc esi
561 dec ecx
562 jnz @1
563 jnc @2
564 inc byte ptr [edi]
565 @2: pop edi
566 pop esi
567 end;
568 {$ELSE}
569 var
570 I : Integer;
571 T, C : ShortInt;
572 begin
573 C := 0;
574 for I := 0 to MantissaDigits do begin
575 T := UB2[I]-UB1[I]-C;
576 if T < 0 then begin
577 C := 1;
578 inc(T, 10);
579 end else
580 C := 0;
581 UB2[I] := T;
582 end;
583 UB2[SigDigits] := C;
584 end;
585 {$ENDIF}
586
587 procedure Unpack(const B : TBcd; var UB : TUnpBcd;
588 var Exponent : Integer; var Sign : Byte);
589 {$IFNDEF UseAsm}
590 var
591 I : Integer;
592 {$ENDIF}
593 begin
594 {$IFDEF UseAsm}
595 asm
596 {$IFDEF VER140}
597 push ecx { get round a compiler bug in D6 }
598 {$ENDIF}
599 push esi
600 push edi
601 mov esi,B
602 mov edi,UB
603 inc esi
604 inc edi
605 mov ecx,BcdSize-1
606 @1: mov al,[esi]
607 inc esi
608 mov ah,al
609 and al,$0F
610 shr ah,4
611 mov [edi],ax
612 inc edi
613 inc edi
614 dec ecx
615 jnz @1
616 xor al,al
617 mov [edi],al
618 pop edi
619 pop esi
620 {$IFDEF VER140}
621 pop ecx { get round a compiler bug in D6 }
622 {$ENDIF}
623 end;
624 {$ELSE}
625 {unpack digits}
626 for I := 1 to BcdSize-1 do begin
627 UB[2*I-1] := B[I] and $F;
628 UB[2*I] := B[I] shr 4;
629 end;
630 {set last overflow digit to zero}
631 UB[2*BcdSize-1] := 0;
632 {$ENDIF}
633
634 {copy sign/exponent}
635 UB[0] := 0;
636 Exponent := B[0] and NoSignBit;
637 Sign := B[0] and SignBit;
638 end;
639
640 {----------------------------------------------------------------------}
641
642 function AbsBcd(const B : TBcd) : TBcd;
643 begin
644 Result := B;
645 Result[0] := B[0] and noSignBit;
646 end;
647
648 function AddBcd(const B1, B2 : TBcd) : TBcd;
649 var
650 E1, E2 : Integer;
651 S1, S2 : Byte;
652 UB1, UB2 : TUnpBcd;
653 begin
654 if B1[0] = 0 then
655 Result := B2
656
657 else if B2[0] = 0 then
658 Result := B1
659
660 else begin
661 Unpack(B1, UB1, E1, S1);
662 Unpack(B2, UB2, E2, S2);
663
664 If E1 < E2 then begin
665 {shift UB1's mantissa to account for smaller exponent}
666 RoundMantissa(UB1, E2-E1-1);
667 ShiftMantissaDown(UB1, E2-E1);
668 end else if E1 > E2 then begin
669 {shift UB2's mantissa to account for smaller exponent}
670 RoundMantissa(UB2, E1-E2-1);
671 ShiftMantissaDown(UB2, E1-E2);
672 E2 := E1;
673 end;
674
675 if S1 <> S2 then begin
676 {differing signs}
677 SubMantissas(UB1, UB2);
678 if UB2[SigDigits] <> 0 then begin
679 {negative result}
680 S2 := S2 xor SignBit;
681 UB2[SigDigits] := 0;
682 NegMantissa(UB2);
683 end;
684 {shift to get rid of any leading zeros}
685 NormalizeMantissa(UB2, E2);
686 end else begin
687 {same signs}
688 AddMantissas(UB1, UB2);
689 if UB2[SigDigits] = 0 then
690 RoundMantissa(UB2, 0);
691 if UB2[SigDigits] <> 0 then begin
692 {temporary overflow}
693 RoundMantissa(UB2, 1);
694 ShiftMantissaDown(UB2, 1);
695 inc(E2);
696 if E2 > NoSignBit then
697 {numeric overflow}
698 RaiseBcdError(stscBcdOverflow);
699 end;
700 end;
701
702 {set sign and exponent}
703 if E2 = 0 then
704 UB2[0] := 0
705 else
706 UB2[0] := S2 or E2;
707
708 Pack(UB2, E2, S2, Result);
709 end;
710 end;
711
712 function BcdExt(const B : TBcd) : Extended;
713 var
714 Code : Integer;
715 S : string[59];
716 begin
717 S := StrExpBcd(B, 0);
718 if ({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator <> '.') then begin
719 while (pos({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator, S) > 0) do
720 S[pos({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator, S)] := '.';
721 end;
722 Val(S, Result, Code);
723 end;
724
725 procedure ConvertBcd(const SrcB; SrcSize : Byte; var DestB; DestSize : Byte);
726 label
727 Repack;
728 type
729 TBA = array[0..40] of Byte; {largest BCD size times 2}
730 PBA = ^TBA;
731 var
732 I, O, Exponent : Integer;
733 PS : PBA;
734 C : Byte;
735 begin
736 if (SrcSize = 0) or (DestSize = 0) then
737 exit;
738
739 Exponent := TBA(SrcB)[0] and NoSignBit;
740
741 {transfer mantissa}
742 if SrcSize <= DestSize then begin
743 {dest is at least as big as src}
744 FillChar(TBA(DestB)[1], DestSize-SrcSize, 0);
745 Move(TBA(SrcB)[1], TBA(DestB)[DestSize-SrcSize+1], SrcSize-1);
746
747 end else begin
748 {need to round src before copying to dest}
749 GetMem(PS, 2*SrcSize);
750
751 {unpack digits}
752 for I := 1 to SrcSize-1 do begin
753 PS^[2*I-1] := TBA(SrcB)[I] and $F;
754 PS^[2*I] := TBA(SrcB)[I] shr 4;
755 end;
756 {set last overflow digit to zero}
757 PS^[2*SrcSize-1] := 0;
758 {O is a shift used when rounding causes an overflow}
759 O := 0;
760
761 {round src starting at most significant lost digit}
762 if PS^[SrcSize-DestSize] >= 5 then begin
763 {rounding has an effect}
764 C := 1;
765 for I := SrcSize-DestSize+1 to 2*(SrcSize-1) do begin
766 inc(PS^[I], C);
767 if PS^[I] > 9 then begin
768 dec(PS^[I], 10);
769 C := 1;
770 end else
771 {done rounding}
772 goto Repack;
773 end;
774 {set overflow digit if we get here}
775 PS^[2*SrcSize-1] := 1;
776 inc(Exponent);
777 O := 1;
778 end;
779
780 Repack:
781 {repack into same buffer taking account of overflow offset}
782 for I := 1 to SrcSize-1 do
783 PS^[I] := PS^[2*I-1+O] or (PS^[2*I+O] shl 4);
784
785 {copy rounded src into dest}
786 Move(PS^[SrcSize-DestSize+1], TBA(DestB)[1], DestSize-1);
787
788 FreeMem(PS, 2*SrcSize);
789 end;
790
791 {copy sign/exponent}
792 TBA(DestB)[0] := Exponent or (TBA(SrcB)[0] and SignBit);
793 end;
794
795 function EqDigitsBcd(const B1, B2 : TBcd; Digits : Cardinal) : Boolean;
796 begin
797 Result := (CmpBcd(RoundDigitsBcd(B1, Digits), RoundDigitsBcd(B2, Digits)) = 0);
798 end;
799
800 function EqPlacesBcd(const B1, B2 : TBcd; Digits : Cardinal) : Boolean;
801 begin
802 Result := (CmpBcd(RoundPlacesBcd(B1, Digits), RoundPlacesBcd(B2, Digits)) = 0);
803 end;
804
805 function CmpBcd(const B1, B2 : TBcd) : Integer;
806 var
807 {$IFNDEF UseAsm}
808 I : Integer;
809 {$ENDIF}
810 E1, E2 : Integer;
811 S1, S2 : Byte;
812 UB1, UB2 : TUnpBcd;
813 begin
814 Unpack(B1, UB1, E1, S1);
815 Unpack(B2, UB2, E2, S2);
816
817 if S1 <> S2 then
818 {signs differ}
819 Result := Integer(S2)-S1
820
821 else begin
822 {signs the same}
823 if E1 <> E2 then
824 {exponents differ}
825 Result := E1-E2
826
827 else if E1 = 0 then
828 {both numbers are zero}
829 Result := 0
830
831 else begin
832 {exponents the same, compare the mantissas}
833 {$IFDEF UseAsm}
834 asm
835 push esi
836 push edi
837 lea esi,UB1+MantissaDigits
838 lea edi,UB2+MantissaDigits
839 mov ecx,MantissaDigits
840 @1: mov al,[esi]
841 sub al,[edi]
842 jnz @2
843 dec esi
844 dec edi
845 dec ecx
846 jnz @1
847 @2: movsx eax,al
848 mov Result,eax
849 pop edi
850 pop esi
851 end;
852 {$ELSE}
853 for I := MantissaDigits downto 1 do begin
854 Result := Integer(UB1[I])-UB2[I];
855 if Result <> 0 then
856 break;
857 end;
858 {$ENDIF}
859 end;
860
861 if S1 <> 0 then
862 {both numbers negative, reverse the result}
863 Result := -Result;
864 end;
865 end;
866
867 function ModBcd(const B1, B2 : TBcd) : TBcd;
868 {-Return B1 mod B2}
869 begin
870 Result := IntBcd(DivBcd(B1, B2));
871 end;
872
873 function DivBcd(const B1, B2 : TBcd) : TBcd;
874 {$IFNDEF UseAsm}
875 label
876 StoreDigit;
877 {$ENDIF}
878 var
879 {$IFNDEF UseAsm}
880 DivIntoCount, I, R : Integer;
881 T, C : ShortInt;
882 DDigit, NDigit : Byte;
883 {$ENDIF}
884 E1, E2, DivDigits, N : Integer;
885 S1, S2 : Byte;
886 UB1, UB2 : TUnpBcd;
887 TB : TIntBcd;
888 begin
889 if B2[0] = 0 then
890 {divide by zero}
891 RaiseBcdError(stscBcdDivByZero);
892
893 if B1[0] = 0 then
894 {numerator is zero, return zero}
895 SetZero(Result)
896
897 else begin
898 Unpack(B1, UB1, E1, S1);
899 Unpack(B2, UB2, E2, S2);
900
901 {TB is the extended numerator}
902 FillChar(TB, 2*BcdSize, 0);
903 Move(UB1[1], TB[2*BcdSize], SigDigits);
904
905 {UB1 is now used to store the result}
906
907 {count significant mantissa digits in divisor}
908 {$IFDEF UseAsm}
909 asm
910 push edi
911 lea edi,UB2+1
912 mov ecx,SigDigits
913 xor al,al
914 repe scasb
915 mov DivDigits,ecx
916 pop edi
917 end;
918 {$ELSE}
919 DivDigits := 0;
920 for I := 1 to MantissaDigits do
921 if UB2[I] <> 0 then begin
922 DivDigits := SigDigits-I;
923 break;
924 end;
925 {$ENDIF}
926
927 if DivDigits = 0 then
928 {divide by zero, shouldn't have gotten here, but just in case...}
929 RaiseBcdError(stscBcdDivByZero);
930
931 {$IFDEF UseAsm}
932 asm
933 push ebx
934 push esi
935 push edi
936 mov ecx,SigDigits {number of digits in result}
937 lea edi,UB1+SigDigits {edi points to MSD of result}
938 lea esi,TB+2*MantissaDigits+1 {esi points to MSD of numerator}
939 mov dh,byte ptr DivDigits {keep DivDigits in dh}
940
941 @1: push ecx {save result counter}
942 push edi {save result position}
943 mov ebx,esi {save numerator position}
944 xor dl,dl {dl = number of times divisor fits into numerator}
945
946 @2: cmp byte ptr [esi+1],0 {check for remainder in numerator}
947 jnz @4 {divisor guaranteed to fit again}
948 xor ecx,ecx
949 mov cl,dh {ecx = number of divisor digits}
950 lea edi,UB2+MantissaDigits {last digit of divisor}
951
952 @3: mov al,[esi] {al = numerator digit}
953 dec esi
954 mov ah,[edi] {ah = divisor digit}
955 dec edi
956 cmp al,ah
957 ja @4 {divisor fits if numerator digit > divisor}
958 jb @7 {doesn't fit if numerator digit < divisor}
959 dec ecx
960 jnz @3
961
962 @4: inc dl {increment number of times divisor fits}
963 mov edi,ebx {restore numerator position to edi}
964 xor ecx,ecx
965 mov cl,dh {ecx = number of divisor digits}
966 lea esi,UB2+MantissaDigits {esi points to MSD of divisor}
967 dec ecx
968 sub esi,ecx {first significant digit of divisor}
969 sub edi,ecx {first active digit of numerator}
970 inc ecx
971 clc {no carry to start}
972
973 @5: mov al,[edi] {al = digit from numerator}
974 sbb al,[esi] {subtract divisor from numerator}
975 aas
976 mov [edi],al {store back to numerator}
977 inc esi
978 inc edi
979 dec ecx
980 jnz @5
981 jnc @6
982 dec byte ptr [edi] {reduce last digit for borrow}
983
984 @6: mov esi,ebx {restore numerator position to esi}
985 jmp @2 {see if divisor fits in numerator again}
986
987 @7: mov esi,ebx {restore numerator position to esi}
988 pop edi {restore result position}
989 pop ecx {restore result counter}
990 mov [edi],dl {store times divisor went into numerator}
991 dec edi {next result digit}
992 dec esi {next numerator digit}
993 dec ecx
994 jnz @1 {compute next result digit}
995
996 pop edi
997 pop esi
998 pop ebx
999 end;
1000 {$ELSE}
1001 {start with most significant digit of numerator}
1002 N := 2*MantissaDigits+1;
1003
1004 {iterate until the result mantissa is filled}
1005 for R := SigDigits downto 1 do begin
1006 DivIntoCount := 0;
1007
1008 repeat
1009 {subtract divisor from current numerator position as many times as possible}
1010 if TB[N+1] = 0 then begin
1011 {no overflow digit in this position of numerator}
1012 for I := 0 to DivDigits-1 do begin
1013 DDigit := UB2[MantissaDigits-I];
1014 NDigit := TB[N-I];
1015 if DDigit < NDigit then
1016 {divisor still fits}
1017 break
1018 else if DDigit > NDigit then
1019 {divisor doesn't fit}
1020 goto StoreDigit;
1021 end;
1022 end;
1023 inc(DivIntoCount);
1024
1025 {subtract divisor once from numerator}
1026 C := 0;
1027 for I := DivDigits-1 downto 0 do begin
1028 T := TB[N-I]-UB2[MantissaDigits-I]-C;
1029 if T < 0 then begin
1030 C := 1;
1031 inc(T, 10);
1032 end else
1033 C := 0;
1034 TB[N-I] := T;
1035 end;
1036 {reduce last digit for borrow}
1037 dec(TB[N+1], C);
1038 until False;
1039
1040 StoreDigit:
1041 {store this digit of result}
1042 UB1[R] := DivIntoCount;
1043 {next numerator digit}
1044 dec(N);
1045 end;
1046 {$ENDIF}
1047
1048 if UB1[SigDigits] <> 0 then begin
1049 {round away the temporary digit}
1050 RoundMantissa(UB1, 1);
1051 ShiftMantissaDown(UB1, 1);
1052 inc(E1);
1053 end;
1054
1055 {compute exponent}
1056 N := E1-E2+ExpBias;
1057 if N > NoSignBit then
1058 {numeric overflow}
1059 RaiseBcdError(stscBcdOverflow);
1060 Pack(UB1, N, S1 xor S2, Result);
1061 end;
1062 end;
1063
1064 function FastVal(const S : string) : TBcd;
1065 {-Internal routine to quickly convert a string constant to a Bcd}
1066 {Assumes no leading spaces,
1067 no leading '+',
1068 no leading '.',
1069 always contains decimal point defined by international DecimalSeparator,
1070 no invalid characters,
1071 no exponent,
1072 < MantissaDigits before decimal point}
1073 var
1074 I, O, Digits, Exponent : Integer;
1075 Sign : Byte;
1076 Rounded : Boolean;
1077 UB : TUnpBcd;
1078
1079 procedure AddDigit(Ch : Char);
1080 begin
1081 if O > 0 then begin
1082 UB[O] := Byte(Ch)-Byte('0');
1083 dec(O);
1084 end else if not Rounded then begin
1085 {got more significant digits than will fit, must round}
1086 Rounded := True;
1087 UB[0] := Byte(Ch)-Byte('0');
1088 RoundMantissa(UB, 0);
1089 if UB[SigDigits] <> 0 then begin
1090 ShiftMantissaDown(UB, 1);
1091 inc(Digits);
1092 end;
1093 end;
1094 end;
1095
1096 begin
1097 FillChar(UB, SizeOf(TUnpBcd), 0);
1098
1099 O := MantissaDigits;
1100 Rounded := False;
1101 Digits := 0;
1102
1103 {get sign if any}
1104 if S[1] = '-' then begin
1105 Sign := SignBit;
1106 I := 2;
1107 end else begin
1108 Sign := 0;
1109 I := 1;
1110 end;
1111
1112 {skip leading zeros}
1113 while S[I] = '0' do
1114 inc(I);
1115
1116 {add significant digits}
1117 while S[I] <> '.' do begin
1118 AddDigit(S[I]);
1119 inc(I);
1120 inc(Digits);
1121 end;
1122
1123 {handle dot}
1124 inc(I);
1125 if Digits = 0 then
1126 {no digits before dot, skip zeros after dot}
1127 while (I <= length(S)) and (S[I] = '0') do begin
1128 inc(I);
1129 dec(Digits);
1130 end;
1131
1132 {add significant digits}
1133 while I <= Length(S) do begin
1134 AddDigit(S[I]);
1135 if Rounded then
1136 break;
1137 inc(I);
1138 end;
1139
1140 {compute final exponent}
1141 Exponent := Digits+ExpBias;
1142
1143 if (Exponent <= 0) or IsZeroMantissa(UB) then
1144 {return zero}
1145 Exponent := 0;
1146
1147 {Return packed result}
1148 Pack(UB, Exponent, Sign, Result);
1149 end;
1150
1151 function ExpBcd(const B : TBcd) : TBcd;
1152 var
1153 MI, Exponent : LongInt;
1154 B1, B2, B3, B4, B5 : TBcd;
1155 begin
1156 if CmpBcd(B, FastVal('147.36')) > 0 then
1157 {numeric overflow}
1158 RaiseBcdError(stscBcdOverflow);
1159
1160 if CmpBcd(B, FastVal('-145.06')) < 0 then begin
1161 {return zero}
1162 SetZero(Result);
1163 Exit;
1164 end;
1165
1166 if B[0] = 0 then begin
1167 {return one}
1168 Result := FastVal('1.0');
1169 Exit;
1170 end;
1171
1172 {If BcdSize > 10, Delphi 2.0 generates a hint (if hints on) about B3 during compile}
1173 {this can be ignored or you can suppress warnings in STDEFINE.INC}
1174 {or suppress hints and warning for the IF..THEN block}
1175
1176 if BcdSize <= 10 then begin
1177 {Burns (Cody-Waite) approximation}
1178 Exponent := RoundBcd(MulBcd(B, FastVal('0.868588963806503655')));
1179 MI := Exponent; {prevent D32 from generating a hint}
1180 B5 := LongBcd(MI);
1181
1182 B3 := AddBcd(B, MulBcd(B5, FastVal('-1.151')));
1183 B1 := AddBcd(B3, MulBcd(B5, FastVal('-0.000292546497022842009')));
1184 B2 := MulBcd(B1, B1);
1185
1186 B3 := MulBcd(B2, FastVal('42.0414268137450315'));
1187 B3 := MulBcd(B2, AddBcd(B3, FastVal('10097.4148724273918')));
1188 B4 := MulBcd(B1, AddBcd(B3, FastVal('333267.029226801611')));
1189
1190 B3 := MulBcd(B2, AddBcd(B2, FastVal('841.243584514154545')));
1191 B3 := MulBcd(B2, AddBcd(B3, FastVal('75739.3346159883444')));
1192 B3 := AddBcd(B3, FastVal('666534.058453603223'));
1193 B3 := DivBcd(B4, SubBcd(B3, B4));
1194 Result := MulBcd(AddBcd(B3, FastVal('0.5')), FastVal('2.0'));
1195
1196 if Odd(MI) then begin
1197 if MI < 0 then
1198 Result := DivBcd(Result, FastVal('3.16227766016837933'))
1199 else
1200 Result := MulBcd(Result, FastVal('3.16227766016837933'));
1201 end;
1202
1203 inc(ShortInt(Result[0]), MI div 2);
1204
1205 end else begin
1206 {series approximation}
1207 {compute B2, a number whose exp is close to 1.0}
1208 {and MI, a number whose exp is a power of 10}
1209 B2 := DivBcd(B, Ln10Bcd);
1210 if B[0] and SignBit <> 0 then
1211 B2 := SubBcd(B2, FastVal('0.5'))
1212 else
1213 B2 := AddBcd(B2, FastVal('0.5'));
1214 MI := TruncBcd(B2);
1215 B2 := SubBcd(B, MulBcd(IntBcd(B2), Ln10Bcd));
1216
1217 {compute exp(B2)}
1218 B1 := FastVal('1.0');
1219 B4 := B1;
1220 Result := B1;
1221 B5 := B2;
1222 while B5[0] and NoSignBit > ExpBias-MantissaDigits-1 do begin
1223 Result := AddBcd(Result, B5);
1224 B4 := AddBcd(B4, B1);
1225 B5 := DivBcd(MulBcd(B5, B2), B4);
1226 end;
1227
1228 {correct exponent for 10**MI}
1229 Exponent := Result[0] and NoSignBit;
1230 inc(Exponent, MI);
1231 if Exponent > NoSignBit then
1232 {numeric overflow}
1233 RaiseBcdError(stscBcdOverflow);
1234 if Exponent <= 0 then
1235 {underflow}
1236 SetZero(Result);
1237 Result[0] := Exponent;
1238 end;
1239 end;
1240
1241 function ExtBcd(E : Extended) : TBcd;
1242 var
1243 S : string;
1244 begin
1245 Str(e:0:MantissaDigits, S);
1246 Result := ValBcd(FastValPrep(S));
1247 end;
1248
1249 function StrGeneralBcd(const B : TBcd) : string;
1250 var
1251 I, EndI, Exponent : Integer;
1252
1253 procedure RemoveTrailingZeros(StartI, EndI : Integer);
1254 var
1255 I : Integer;
1256 begin
1257 I := StartI;
1258 while (I > 0) and (Result[I] = '0') and (Result[I] <> {$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator) do
1259 dec(I);
1260 if Result[I] = {$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator then
1261 dec(I);
1262 Delete(Result, I+1, EndI-I);
1263 end;
1264
1265 begin
1266 Exponent := B[0] and NoSignBit;
1267
1268 if (Exponent = 0) or
1269 ((Exponent <= MantissaDigits+ExpBias) and (Exponent >= ExpBias-4)) then begin
1270 {use fixed point format for zero, digits to left of decimal point greater
1271 than or equal to MantissaDigits, or value greater than 0.00001}
1272 Result := StrBcd(B, 0, MantissaDigits);
1273 RemoveTrailingZeros(Length(Result), Length(Result));
1274
1275 end else begin
1276 {otherwise use scientific format}
1277 Result := StrExpBcd(B, 0);
1278 if Result[1] = ' ' then
1279 Delete(Result, 1, 1);
1280 I := Length(Result)-1;
1281 EndI := I-3;
1282 while (I <= Length(Result)) and (Result[I] = '0') do
1283 Delete(Result, I, 1);
1284 if I > Length(Result) then begin
1285 {exponent was all zero}
1286 Delete(Result, Length(Result)-1, 2);
1287 I := Length(Result);
1288 end else
1289 {skip back over "e+"}
1290 I := EndI;
1291 RemoveTrailingZeros(I, EndI);
1292 end;
1293 end;
1294
1295 function FormatBcd(const Format: string; const B : TBcd): string;
1296 label
1297 Restart;
1298 var
1299 SectNum, SectOfs, I, ExpDigits, ActPlaces : Integer;
1300 DigitCount, DecimalIndex, FirstDigit, LastDigit : Integer;
1301 DigitPlace, DigitDelta, Exponent : Integer;
1302 BufOfs, UBOfs : Integer;
1303 ThousandSep, Scientific : Boolean;
1304 Ch : Char;
1305 Sign : Byte;
1306 UB : TUnpBcd;
1307 SExponent : string;//[4];
1308 Buffer : array[0..255] of Char;
1309
1310 function FindSection(SectNum : Integer) : Integer;
1311 {-Return the offset into Format for the given section number}
1312 var
1313 Ch : Char;
1314 begin
1315 if SectNum > 0 then begin
1316 Result := 1;
1317 while Result <= Length(Format) do begin
1318 Ch := Format[Result];
1319 case Ch of
1320 {labels in ASCII order so 32-bit compiler generates better code}
1321 '"', '''' : {skip literal}
1322 begin
1323 inc(Result);
1324 while (Result <= Length(Format)) and (Format[Result] <> Ch) do
1325 inc(Result);
1326 if Result > Length(Format) then
1327 break;
1328 end;
1329 ';' : {end of section}
1330 begin
1331 dec(SectNum);
1332 if SectNum = 0 then begin
1333 inc(Result);
1334 if (Result > Length(Format)) or (Format[Result] = ';') then
1335 {empty section}
1336 break
1337 else
1338 {found the section, return its offset}
1339 exit;
1340 end;
1341 end;
1342 end;
1343 inc(Result);
1344 end;
1345 end;
1346
1347 {arrive here if desired section is empty, not found, or ill-formed}
1348 if (Length(Format) = 0) or (Format[1] = ';') then
1349 {first section is empty, use general format}
1350 Result := 0
1351 else
1352 {use first section}
1353 Result := 1;
1354 end;
1355
1356 procedure ScanSection(SectOfs : Integer);
1357 {-Initialize DigitCount, DecimalIndex, ThousandSep,
1358 Scientific, FirstDigit, LastDigit}
1359 var
1360 FirstZero, LastZero : Integer;
1361 Ch : Char;
1362 begin
1363 FirstZero := 32767;
1364 LastZero := 0;
1365 DigitCount := 0;
1366 DecimalIndex := -1;
1367 ThousandSep := False;
1368 Scientific := False;
1369
1370 repeat
1371 Ch := Format[SectOfs];
1372 case Ch of
1373 {labels in ASCII order so 32-bit compiler generates better code}
1374 '"' :
1375 begin
1376 inc(SectOfs);
1377 while (SectOfs <= Length(Format)) and (Format[SectOfs] <> Ch) do
1378 inc(SectOfs);
1379 if SectOfs > Length(Format) then
1380 break;
1381 end;
1382
1383 '#' :
1384 inc(DigitCount);
1385
1386 '''' :
1387 begin
1388 inc(SectOfs);
1389 while (SectOfs <= Length(Format)) and (Format[SectOfs] <> Ch) do
1390 inc(SectOfs);
1391 if SectOfs > Length(Format) then
1392 break;
1393 end;
1394
1395 '0' :
1396 begin
1397 if DigitCount < FirstZero then
1398 FirstZero := DigitCount;
1399 inc(DigitCount);
1400 LastZero := DigitCount;
1401 end;
1402
1403 ';' :
1404 break;
1405
1406 'E', 'e' :
1407 if SectOfs < Length(Format) then begin
1408 inc(SectOfs);
1409 case Format[SectOfs] of
1410 '-', '+' :
1411 begin
1412 Scientific := True;
1413 repeat
1414 inc(SectOfs);
1415 until (SectOfs > Length(Format)) or (Format[SectOfs] <> '0');
1416 end;
1417 else
1418 {back up and look at character after 'e' again}
1419 dec(SectOfs);
1420 end;
1421 end;
1422 else
1423 if Ch = {$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}ThousandSeparator then
1424 ThousandSep := True;
1425
1426 if Ch = {$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator then
1427 if DecimalIndex = -1 then
1428 DecimalIndex := DigitCount;
1429 end;
1430
1431 inc(SectOfs);
1432 if SectOfs > Length(Format) then
1433 break;
1434 until False;
1435
1436 if DecimalIndex = -1 then
1437 DecimalIndex := DigitCount;
1438 LastDigit := DecimalIndex-LastZero;
1439 if LastDigit > 0 then
1440 LastDigit := 0;
1441 FirstDigit := DecimalIndex-FirstZero;
1442 if FirstDigit < 0 then
1443 FirstDigit := 0;
1444 end;
1445
1446 procedure StoreChar(Ch : Char);
1447 begin
1448 if BufOfs >= Length(Buffer) then
1449 {buffer overrun}
1450 RaiseBcdError(stscBcdBufOverflow);
1451 Buffer[BufOfs] := Ch;
1452 inc(BufOfs);
1453 end;
1454
1455 procedure StoreDigitReally(ReadUB : Boolean);
1456 var
1457 BVal : Byte;
1458 begin
1459 if ReadUB then begin
1460 if UBOfs >= 0 then begin
1461 BVal := UB[UBOfs];
1462 dec(UBOfs);
1463 end else if DigitPlace <= LastDigit then begin
1464 dec(DigitPlace);
1465 Exit;
1466 end else
1467 BVal := 0;
1468 end else
1469 BVal := 0;
1470
1471 if DigitPlace = 0 then begin
1472 StoreChar({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator);
1473 StoreChar(Char(BVal+Byte('0')));
1474 end else begin
1475 StoreChar(Char(BVal+Byte('0')));
1476 if ThousandSep then
1477 if DigitPlace > 1 then
1478 if DigitPlace mod 3 = 1 then
1479 StoreChar({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}ThousandSeparator);
1480 end;
1481
1482 dec(DigitPlace);
1483 end;
1484
1485 procedure StoreDigit;
1486 begin
1487 if DigitDelta = 0 then
1488 StoreDigitReally(True)
1489 else if DigitDelta < 0 then begin
1490 inc(DigitDelta);
1491 if DigitPlace <= FirstDigit then
1492 StoreDigitReally(False)
1493 else
1494 dec(DigitPlace);
1495 end else begin
1496 repeat
1497 StoreDigitReally(True);
1498 dec(DigitDelta);
1499 until DigitDelta = 0;
1500 StoreDigitReally(True);
1501 end;
1502 end;
1503
1504 begin
1505 Unpack(B, UB, Exponent, Sign);
1506
1507 Restart:
1508 if Exponent = 0 then
1509 {zero}
1510 SectNum := 2
1511 else if Sign <> 0 then
1512 {negative}
1513 SectNum := 1
1514 else
1515 {positive}
1516 SectNum := 0;
1517 SectOfs := FindSection(SectNum);
1518
1519 if SectOfs = 0 then
1520 {general floating point format}
1521 Result := StrGeneralBcd(B)
1522
1523 else begin
1524 {scan the section once to determine critical format properties}
1525 ScanSection(SectOfs);
1526
1527 if Exponent <> 0 then begin
1528 {round based on number of displayed digits}
1529 ActPlaces := Integer(MantissaDigits)-Exponent+ExpBias;
1530 if DigitCount-DecimalIndex < ActPlaces then begin
1531 RoundMantissa(UB, ActPlaces-(DigitCount-DecimalIndex));
1532 if UB[SigDigits] <> 0 then begin
1533 ShiftMantissaDown(UB, 1);
1534 inc(Exponent);
1535 end else if IsZeroMantissa(UB) then begin
1536 {rounded to zero, possibly use a different mask}
1537 Exponent := 0;
1538 goto Restart;
1539 end;
1540 end;
1541 end;
1542
1543 {apply formatting}
1544 if Scientific then begin
1545 DigitPlace := DecimalIndex;
1546 DigitDelta := 0;
1547 if Exponent = 0 then
1548 {for input = 0, display E+00}
1549 Exponent := ExpBias+1
1550 end else begin
1551 if Exponent = 0 then
1552 {special case for input = 0}
1553 Exponent := ExpBias
1554 else if Exponent-ExpBias > MantissaDigits then begin
1555 {all digits are integer part}
1556 Result := StrGeneralBcd(B);
1557 Exit;
1558 end;
1559 DigitPlace := Exponent-ExpBias;
1560 DigitDelta := DigitPlace-DecimalIndex;
1561 if DigitPlace < DecimalIndex then
1562 DigitPlace := DecimalIndex;
1563 end;
1564
1565 BufOfs := 0;
1566 UBOfs := MantissaDigits;
1567
1568 if Sign <> 0 then
1569 if SectOfs = 1 then
1570 StoreChar('-');
1571
1572 repeat
1573 Ch := Format[SectOfs];
1574 case Ch of
1575 {labels in ASCII order so 32-bit compiler generates better code}
1576 '"' :
1577 begin
1578 inc(SectOfs);
1579 while (SectOfs <= Length(Format)) and (Format[SectOfs] <> Ch) do begin
1580 StoreChar(Format[SectOfs]);
1581 inc(SectOfs);
1582 end;
1583 if SectOfs > Length(Format) then
1584 break;
1585 end;
1586 '#' :
1587 StoreDigit;
1588
1589 '''' :
1590 begin
1591 inc(SectOfs);
1592 while (SectOfs <= Length(Format)) and (Format[SectOfs] <> Ch) do begin
1593 StoreChar(Format[SectOfs]);
1594 inc(SectOfs);
1595 end;
1596 if SectOfs > Length(Format) then
1597 break;
1598 end;
1599
1600 '0' :
1601 StoreDigit;
1602
1603 ';' :
1604 break;
1605
1606 'E', 'e' :
1607 if SectOfs < Length(Format) then begin
1608 inc(SectOfs);
1609 case Format[SectOfs] of
1610 '-', '+' :
1611 begin
1612 StoreChar(Ch);
1613 Ch := Format[SectOfs];
1614 ExpDigits := -1;
1615 repeat
1616 inc(ExpDigits);
1617 inc(SectOfs);
1618 until (SectOfs > Length(Format)) or (Format[SectOfs] <> '0');
1619 if ExpDigits > 4 then
1620 ExpDigits := 4;
1621 dec(Exponent, ExpBias+DecimalIndex);
1622 if (Exponent >= 0) and (Ch = '+') then
1623 StoreChar('+');
1624 if Exponent < 0 then begin
1625 StoreChar('-');
1626 Exponent := Abs(Exponent);
1627 end;
1628 Str(Exponent:ExpDigits, SExponent);
1629 for I := 1 to ExpDigits do
1630 if SExponent[I] = ' ' then
1631 StoreChar('0')
1632 else
1633 StoreChar(SExponent[I]);
1634 end;
1635 else
1636 StoreChar(Ch);
1637 StoreChar(Format[SectOfs]);
1638 end;
1639 end else
1640 StoreChar(Ch);
1641 else
1642 {these characters are automatically inserted in StoreDigit};
1643 if not (Ch in [{$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}ThousandSeparator, {$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator]) then
1644 StoreChar(Ch);
1645 end;
1646
1647 inc(SectOfs);
1648 if SectOfs > Length(Format) then
1649 break;
1650 until False;
1651
1652 SetLength(Result, BufOfs);
1653 move(Buffer[0], Result[1], BufOfs * SizeOf(Char));
1654 end;
1655 end;
1656
1657 function FracBcd(const B : TBcd) : TBcd;
1658 begin
1659 Result := SubBcd(B, IntBcd(B));
1660 end;
1661
1662 function IsIntBcd(const B : TBcd) : Boolean;
1663 var
1664 {$IFNDEF UseAsm}
1665 I : Integer;
1666 {$ENDIF}
1667 Exponent : Integer;
1668 Sign : Byte;
1669 UB : TUnpBcd;
1670 begin
1671 Unpack(B, UB, Exponent, Sign);
1672
1673 if Exponent = 0 then
1674 {0.0 has no fractional part}
1675 Result := True
1676
1677 else if Exponent <= ExpBias then
1678 {value is less than one, but non-zero}
1679 Result := False
1680
1681 else if Exponent-ExpBias >= MantissaDigits then
1682 {entire mantissa is non-fractional}
1683 Result := True
1684
1685 else begin
1686 {see if any non-zero digits to left of decimal point}
1687 {$IFDEF UseAsm}
1688 asm
1689 push edi
1690 lea edi,UB+1
1691 mov ecx,MantissaDigits+ExpBias
1692 sub ecx,Exponent
1693 xor al,al
1694 cld
1695 repe scasb
1696 jne @1
1697 inc al
1698 @1: mov Result,al
1699 pop edi
1700 end;
1701 {$ELSE}
1702 for I := 1 to MantissaDigits-(Exponent-ExpBias) do
1703 if UB[I] <> 0 then begin
1704 Result := False;
1705 Exit;
1706 end;
1707 Result := True;
1708 {$ENDIF}
1709 end;
1710 end;
1711
1712 function IntBcd(const B : TBcd) : TBcd;
1713 var
1714 Exponent : Integer;
1715 Sign : Byte;
1716 UB : TUnpBcd;
1717 begin
1718 Unpack(B, UB, Exponent, Sign);
1719
1720 if Exponent <= ExpBias then
1721 {value is less than one}
1722 SetZero(Result)
1723
1724 else if Exponent-ExpBias >= MantissaDigits then
1725 {entire mantissa is integer part}
1726 Result := B
1727
1728 else begin
1729 {clear fractional digits}
1730 FillChar(UB[1], MantissaDigits-(Exponent-ExpBias), 0);
1731 Pack(UB, Exponent, Sign, Result);
1732 end;
1733 end;
1734
1735 function IntPowBcd(const B : TBcd; E : LongInt) : TBcd;
1736 var
1737 I : LongInt;
1738 B1 : TBcd;
1739 begin
1740 B1 := FastVal('1.0');
1741 Result := B1;
1742 for I := 1 to Abs(E) do
1743 Result := MulBcd(Result, B);
1744 if E < 0 then
1745 Result := DivBcd(B1, Result);
1746 end;
1747
1748 function LnBcd20(const B : TBcd) : TBcd;
1749 const
1750 Iterations = 9;
1751 var
1752 Exponent, N, K : integer;
1753 BN, B025, B05, B1, AN, GN, Pow : TBcd;
1754 DN1, DN : array[0..Iterations] of TBcd;
1755 begin
1756 {normalize input in range 0.10-0.99...}
1757 Exponent := B[0]-ExpBias;
1758 BN := B;
1759 BN[0] := ExpBias;
1760
1761 {initialize some constants}
1762 B025 := FastVal('0.25');
1763 B05 := FastVal('0.5');
1764 B1 := FastVal('1.0');
1765
1766 {compute initial terms of approximation}
1767 AN := MulBcd(B05, AddBcd(BN, B1));
1768 GN := SqrtBcd(BN);
1769 DN1[0] := AN;
1770
1771 {converge on exact value}
1772 for N := 1 to Iterations do begin
1773 AN := MulBcd(B05, AddBcd(AN, GN));
1774 DN[0] := AN;
1775 Pow := B025;
1776 for K := 1 to N do begin
1777 DN[K] := DivBcd(SubBcd(DN[K-1], MulBcd(Pow, DN1[K-1])), SubBcd(B1, Pow));
1778 if K = N then
1779 break;
1780 Pow := MulBcd(Pow, B025);
1781 end;
1782
1783 if N = Iterations then
1784 break;
1785 GN := SqrtBcd(MulBcd(AN, GN));
1786 DN1 := DN;
1787 end;
1788 Result := DivBcd(SubBcd(BN, B1), DN[Iterations]);
1789
1790 {correct for normalization}
1791 Result := AddBcd(Result, MulBcd(LongBcd(Exponent), Ln10Bcd));
1792 end;
1793
1794 function LnBcd10(const B : TBcd) : TBcd;
1795 var
1796 Exponent : Integer;
1797 BN, B1, S, W, T, AW, BW : TBcd;
1798 begin
1799 {normalize input in range 0.10-0.99...}
1800 Exponent := B[0]-ExpBias;
1801 BN := B;
1802 BN[0] := ExpBias;
1803
1804 if CmpBcd(BN, FastVal('0.316227766016837933')) < 0 then begin
1805 {renormalize in range .316-3.16}
1806 dec(Exponent);
1807 inc(BN[0]);
1808 end;
1809
1810 B1 := FastVal('1.0');
1811 S := DivBcd(SubBcd(BN, B1), AddBcd(BN, B1));
1812 W := MulBcd(S, S);
1813
1814 T := MulBcd(W, FastVal('-0.741010784161919239'));
1815 T := MulBcd(W, AddBcd(T, FastVal('10.3338571514793865')));
1816 T := MulBcd(W, AddBcd(T, FastVal('-39.273741020315625')));
1817 T := MulBcd(W, AddBcd(T, FastVal('55.4085912041205931')));
1818 AW := AddBcd(T, FastVal('-26.0447002405557636'));
1819
1820 T := MulBcd(W, AddBcd(W, FastVal('-19.3732345832854786')));
1821 T := MulBcd(W, AddBcd(T, FastVal('107.109789115668009')));
1822 T := MulBcd(W, AddBcd(T, FastVal('-244.303035341829542')));
1823 T := MulBcd(W, AddBcd(T, FastVal('245.347618868489348')));
1824 BW := AddBcd(T, FastVal('-89.9552077881033117'));
1825
1826 T := MulBcd(W, DivBcd(AW, BW));
1827 T := MulBcd(S, AddBcd(T, FastVal('0.868588963806503655')));
1828
1829 Result := MulBcd(AddBcd(T, LongBcd(Exponent)), Ln10Bcd);
1830 end;
1831
1832 function LnBcd(const B : TBcd) : TBcd;
1833 begin
1834 if (B[0] = 0) or (B[0] and SignBit <> 0) then
1835 {ln of zero or a negative number}
1836 RaiseBcdError(stscBcdBadInput);
1837
1838 if BcdSize <= 10 then
1839 Result := LnBcd10(B)
1840 else
1841 Result := LnBcd20(B);
1842 end;
1843
1844 function LongBcd(L : LongInt) : TBcd;
1845 var
1846 S : string;
1847 begin
1848 Str(L, S);
1849 Result := ValBcd(FastValPrep(S));
1850 end;
1851
1852 function MulBcd(const B1, B2 : TBcd) : TBcd;
1853 var
1854 E1, E2, Digits : Integer;
1855 S1, S2 : Byte;
1856 {$IFNDEF UseAsm}
1857 I1, I2 : Integer;
1858 CP, CN : Byte;
1859 T, T1, T2 : Byte;
1860 {$ENDIF}
1861 PB : PUnpBcd;
1862 UB1, UB2 : TUnpBcd;
1863 TB : TIntBcd;
1864 begin
1865 if (B1[0] = 0) or (B2[0] = 0) then
1866 SetZero(Result)
1867
1868 else begin
1869 Unpack(B1, UB1, E1, S1);
1870 Unpack(B2, UB2, E2, S2);
1871
1872 FillChar(TB, SizeOf(TIntBcd), 0);
1873
1874 {multiply and sum the mantissas}
1875 {$IFDEF UseAsm}
1876 asm
1877 push ebx
1878 push esi
1879 push edi
1880 lea ebx,UB1 {multiplier}
1881 lea edi,TB {result}
1882 mov ecx,MantissaDigits
1883
1884 @1: inc ebx {next multiplier digit}
1885 inc edi {next output digit}
1886 mov al,[ebx] {get next multiplier digit}
1887 or al,al {if zero, nothing to do}
1888 jz @3
1889
1890 push ecx {save digit counter}
1891 mov dl,al {save multiplier}
1892 lea esi,UB2+1 {multiplicand}
1893 mov ecx,MantissaDigits
1894 xor dh,dh
1895
1896 @2: mov al,[esi] {next multiplicand digit}
1897 inc esi
1898 mul dl {multiply by multiplier, overflow in ah}
1899 aam
1900 add al,[edi] {add previous result}
1901 aaa
1902 add al,dh {add previous overflow}
1903 aaa
1904 mov [edi],al {store temporary result}
1905 inc edi
1906 mov dh,ah {save overflow for next time}
1907 dec ecx
1908 jnz @2
1909 mov [edi],dh {save last overflow in next digit}
1910 sub edi,MantissaDigits {reset output offset for next multiplier}
1911 pop ecx
1912
1913 @3: dec ecx {next multiplier digit}
1914 jnz @1
1915 pop edi
1916 pop esi
1917 pop ebx
1918 end;
1919 {$ELSE}
1920 for I1 := 1 to MantissaDigits do begin
1921 T1 := UB1[I1];
1922 if T1 <> 0 then begin
1923 CP := 0;
1924 for I2 := 1 to MantissaDigits do begin
1925 T := T1*UB2[I2];
1926 T2 := T mod 10;
1927 CN := T div 10;
1928 inc(T2, TB[I1+I2-1]);
1929 if T2 > 9 then begin
1930 dec(T2, 10);
1931 inc(CN);
1932 end;
1933 inc(T2, CP);
1934 if T2 > 9 then begin
1935 dec(T2, 10);
1936 inc(CN);
1937 end;
1938 TB[I1+I2-1] := T2;
1939 CP := CN;
1940 end;
1941 {store last carry in next digit of buffer}
1942 TB[I1+MantissaDigits] := CP;
1943 end;
1944 end;
1945 {$ENDIF}
1946
1947 {normalize the product}
1948 if TB[2*MantissaDigits] <> 0 then begin
1949 PB := PUnpBcd(@TB[MantissaDigits]);
1950 Digits := 0;
1951 end else begin
1952 PB := PUnpBcd(@TB[MantissaDigits-1]);
1953 Digits := -1;
1954 end;
1955 RoundMantissa(PB^, 0);
1956 if PB^[SigDigits] <> 0 then begin
1957 inc(PByte(PB));
1958 inc(Digits);
1959 end;
1960 {copy back to UB2}
1961 UB2 := PB^;
1962
1963 {set sign and exponent}
1964 inc(E2, E1+Digits-ExpBias);
1965 if E2 > NoSignBit then
1966 {numeric overflow}
1967 RaiseBcdError(stscBcdOverflow);
1968
1969 Pack(UB2, E2, S1 xor S2, Result);
1970 end;
1971 end;
1972
1973 function NegBcd(const B : TBcd) : TBcd;
1974 begin
1975 Result := B;
1976 if B[0] <> 0 then
1977 Result[0] := B[0] xor SignBit;
1978 end;
1979
1980 function PowBcd(const B, E : TBcd) : TBcd;
1981 begin
1982 if E[0] = 0 then
1983 {anything raised to the zero power is 1.0}
1984 Result := FastVal('1.0')
1985
1986 else if IsIntBcd(E) then
1987 {compute the power by simple multiplication}
1988 Result := IntPowBcd(B, TruncBcd(E))
1989
1990 else begin
1991 if B[0] and SignBit <> 0 then
1992 {negative number raised to a non-integer power}
1993 RaiseBcdError(stscBcdBadInput);
1994
1995 Result := ExpBcd(MulBcd(E, LnBcd(B)));
1996 end;
1997 end;
1998
1999 function RoundBcd(const B : TBcd) : LongInt;
2000 var
2001 Exponent, I : Integer;
2002 Sign : Byte;
2003 UB : TUnpBcd;
2004 begin
2005 Unpack(B, UB, Exponent, Sign);
2006
2007 Result := 0;
2008 if Exponent <> 0 then begin
2009 {Bcd is not zero}
2010 I := MantissaDigits;
2011 {add digits to left of decimal point}
2012 while (I >= 1) and (Exponent > ExpBias) do begin
2013 if Abs(Result) > MaxLongInt div 10 then
2014 {numeric overflow}
2015 RaiseBcdError(stscBcdOverflow);
2016 Result := 10*Result;
2017 if Sign <> 0 then begin
2018 if Result < -MaxLongInt-1+UB[I] then
2019 {numeric overflow}
2020 RaiseBcdError(stscBcdOverflow);
2021 dec(Result, UB[I]);
2022 end else begin
2023 if Result > MaxLongInt-UB[I] then
2024 {numeric overflow}
2025 RaiseBcdError(stscBcdOverflow);
2026 inc(Result, UB[I]);
2027 end;
2028 dec(I);
2029 dec(Exponent);
2030 end;
2031
2032 {round last digit}
2033 if (I >= 1) and (Exponent = ExpBias) and (UB[I] >= 5) then begin
2034 if Sign <> 0 then begin
2035 if Result = -MaxLongInt-1 then
2036 {numeric overflow}
2037 RaiseBcdError(stscBcdOverflow);
2038 dec(Result);
2039 end else begin
2040 if Result = MaxLongInt then
2041 {numeric overflow}
2042 RaiseBcdError(stscBcdOverflow);
2043 inc(Result);
2044 end;
2045 end;
2046
2047 end;
2048 end;
2049
2050 function RoundDigitsBcd(const B : TBcd; Digits : Cardinal) : TBcd;
2051 var
2052 Exponent : Integer;
2053 Sign : Byte;
2054 UB : TUnpBcd;
2055 begin
2056 if B[0] = 0 then
2057 {input is zero}
2058 SetZero(Result)
2059
2060 else if Digits >= MantissaDigits then
2061 {no actual rounding}
2062 Result := B
2063
2064 else begin
2065 Unpack(B, UB, Exponent, Sign);
2066
2067 {treat 0 digits same as 1}
2068 if Digits = 0 then
2069 Digits := 1;
2070
2071 RoundMantissa(UB, MantissaDigits-Digits);
2072 if UB[SigDigits] <> 0 then begin
2073 ShiftMantissaDown(UB, 1);
2074 inc(Exponent);
2075 end else if IsZeroMantissa(UB) then
2076 Exponent := 0;
2077
2078 Pack(UB, Exponent, Sign, Result);
2079 end;
2080 end;
2081
2082 function RoundPlacesBcd(const B : TBcd; Places : Cardinal) : TBcd;
2083 var
2084 Exponent, ActPlaces : Integer;
2085 Sign : Byte;
2086 UB : TUnpBcd;
2087 begin
2088 if B[0] = 0 then
2089 {input is zero}
2090 SetZero(Result)
2091
2092 else begin
2093 ActPlaces := Integer(MantissaDigits)-(B[0] and NoSignBit)+ExpBias;
2094
2095 if LongInt(Places) >= ActPlaces then
2096 {no actual rounding}
2097 Result := B
2098
2099 else begin
2100 Unpack(B, UB, Exponent, Sign);
2101
2102 RoundMantissa(UB, ActPlaces-LongInt(Places));
2103 if UB[SigDigits] <> 0 then begin
2104 ShiftMantissaDown(UB, 1);
2105 inc(Exponent);
2106 end else if IsZeroMantissa(UB) then
2107 Exponent := 0;
2108
2109 Pack(UB, Exponent, Sign, Result);
2110 end;
2111 end;
2112 end;
2113
2114 function SqrtBcd(const B : TBcd) : TBcd;
2115 var
2116 Exponent, I, Iterations : Integer;
2117 BN, B05 : TBcd;
2118 begin
2119 if B[0] and SignBit <> 0 then
2120 {square root of a negative number}
2121 RaiseBcdError(stscBcdBadInput);
2122
2123 if B[0] = 0 then begin
2124 {done for input of zero}
2125 SetZero(Result);
2126 Exit;
2127 end;
2128
2129 {normalize input}
2130 Exponent := B[0]-ExpBias;
2131 BN := B;
2132 BN[0] := ExpBias;
2133
2134 {create reused constant bcd}
2135 B05 := FastVal('0.5');
2136
2137 {compute initial approximation of sqrt}
2138 Result := AddBcd(MulBcd(FastVal('0.894470'), BN),
2139 FastVal('0.223607'));
2140
2141 if BcdSize <= 10 then
2142 Iterations := 3
2143 else
2144 Iterations := 5;
2145
2146 {iterate to accurate normalized sqrt, Result = 0.5*((BN/Result)+Result)}
2147 for I := 1 to Iterations do
2148 Result := MulBcd(AddBcd(DivBcd(BN, Result), Result), B05);
2149
2150 {final correction Result = (0.5*(BN/Result-Result))+Result}
2151 Result := AddBcd(MulBcd(SubBcd(DivBcd(BN, Result), Result), B05), Result);
2152
2153 if Odd(Exponent) then begin
2154 Result := MulBcd(Result,
2155 FastVal('0.31622776601683793319988935444327185337')); {Sqrt(0.1)}
2156 inc(Exponent);
2157 end;
2158
2159 inc(Result[0], Exponent shr 1);
2160 end;
2161
2162 function StrBcd(const B : TBcd; Width, Places : Cardinal) : string;
2163 var
2164 I, O, Exponent, ActWidth, Digits, DecimalPos : Integer;
2165 Sign : Byte;
2166 UB : TUnpBcd;
2167
2168 procedure AddChar(Ch : Char);
2169 begin
2170 Result[O] := Ch;
2171 inc(O);
2172 end;
2173
2174 begin
2175 Unpack(B, UB, Exponent, Sign);
2176
2177 if Exponent = 0 then begin
2178 {ensure mantissa is set to zero}
2179 FillChar(UB[1], SigDigits, 0);
2180 {fool the rest of the function}
2181 Exponent := ExpBias+1;
2182 end;
2183
2184 {ActWidth is the non-padded width}
2185 {it has at least one digit before decimal point}
2186 ActWidth := 1;
2187 if Exponent > ExpBias+1 then
2188 {add other digits before decimal point}
2189 inc(ActWidth, Exponent-ExpBias-1);
2190
2191 {add digits after decimal point}
2192 inc(ActWidth, Places);
2193
2194 {see how many digits from mantissa to use}
2195 if Exponent < ExpBias+1 then begin
2196 Digits := LongInt(Places)-(ExpBias-Exponent);
2197 if Digits < 0 then
2198 Digits := 0;
2199 end else
2200 Digits := ActWidth;
2201
2202 if Places <> 0 then
2203 {add one for decimal point}
2204 inc(ActWidth);
2205
2206 if Sign <> 0 then
2207 {add one for minus sign}
2208 inc(ActWidth);
2209
2210 if Digits < MantissaDigits then begin
2211 {need to round}
2212 RoundMantissa(UB, MantissaDigits-Digits);
2213 if UB[SigDigits] <> 0 then begin
2214 ShiftMantissaDown(UB, 1);
2215 inc(Exponent);
2216 inc(Digits);
2217 if Exponent > ExpBias+1 then
2218 inc(ActWidth);
2219 end;
2220 end else
2221 {use all mantissa digits}
2222 Digits := MantissaDigits;
2223
2224 {adjust and limit Width}
2225 if Width = 0 then
2226 Width := ActWidth;
2227 {$IFDEF WStrings}
2228 if Width > 255 then
2229 Width := 255;
2230 {$ENDIF}
2231 SetLength(Result, Width);
2232
2233 if LongInt(Width) < ActWidth then begin
2234 {result won't fit in specified width}
2235 Result := StringOfChar(OverflowChar, Length(Result)); //FillChar(Result[1], Length(Result) * SizeOf(Char), OverflowChar);
2236 Exit;
2237 end;
2238
2239 if LongInt(Width) > ActWidth then begin
2240 {store leading spaces}
2241 StrPCopy(PChar(Result), StringOfChar(' ', LongInt(Width)-ActWidth)); //FillChar(Result[1], LongInt(Width)-ActWidth, ' ');
2242 O := LongInt(Width)-ActWidth+1;
2243 end else
2244 O := 1;
2245
2246 if Sign <> 0 then
2247 AddChar('-');
2248
2249 if Exponent < ExpBias+1 then begin
2250 {number is less than 1}
2251 AddChar('0');
2252 if Exponent <> 0 then begin
2253 AddChar({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator);
2254 for I := 1 to ExpBias-Exponent do
2255 if O <= LongInt(Width) then
2256 AddChar('0');
2257 end;
2258 end;
2259
2260 if Places = 0 then
2261 {no decimal point}
2262 DecimalPos := 0
2263 else
2264 DecimalPos := Width-Places;
2265
2266 {add digits from the mantissa}
2267 if Digits <> 0 then begin
2268 I := SigDigits;
2269 if UB[I] = 0 then
2270 dec(I);
2271 while (Digits > 0) and (O <= LongInt(Width)) do begin
2272 if O = DecimalPos then
2273 AddChar({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator);
2274 AddChar(Char(UB[I]+Byte('0')));
2275 dec(I);
2276 dec(Digits);
2277 end;
2278 end;
2279
2280 {add trailing zeros, if any}
2281 while O <= LongInt(Width) do begin
2282 if O = DecimalPos then
2283 AddChar({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator);
2284 if O <= LongInt(Width) then
2285 AddChar('0');
2286 end;
2287 end;
2288
2289 function StrExpBcd(const B : TBcd; Width : Cardinal) : string;
2290 const
2291 MinWidth = 8;
2292 MaxWidth = MantissaDigits+6;
2293 var
2294 I, O, Exponent : Integer;
2295 Sign : Byte;
2296 UB : TUnpBcd;
2297
2298 procedure AddChar(Ch : Char);
2299 begin
2300 Result[O] := Ch;
2301 inc(O);
2302 end;
2303
2304 begin
2305 Unpack(B, UB, Exponent, Sign);
2306
2307 {validate and adjust Width}
2308 if Width = 0 then
2309 Width := MaxWidth
2310 else if Width < MinWidth then
2311 Width := MinWidth;
2312 {$IFDEF WStrings}
2313 if Width > 255 then
2314 Width := 255;
2315 {$ENDIF}
2316 SetLength(Result, Width);
2317
2318 {store leading spaces}
2319 if Width > MaxWidth then begin
2320 StrPCopy(PChar(Result), StringOfChar(' ', Width-MaxWidth)); //FillChar(Result[1], Width-MaxWidth, ' ');
2321 O := Width-MaxWidth+1;
2322 end else
2323 O := 1;
2324
2325 {store sign}
2326 if Sign <> 0 then
2327 AddChar('-')
2328 else
2329 AddChar(' ');
2330
2331 if Exponent = 0 then begin
2332 {ensure mantissa is set to zero}
2333 FillChar(UB[1], SigDigits, 0);
2334 {force Exponent to display as 0}
2335 Exponent := ExpBias+1;
2336
2337 end else if Width < MaxWidth then begin
2338 {need to round}
2339 RoundMantissa(UB, MaxWidth-Width);
2340 if UB[SigDigits] <> 0 then begin
2341 ShiftMantissaDown(UB, 1);
2342 inc(Exponent);
2343 end;
2344 end;
2345
2346 {copy mantissa to string}
2347 I := MantissaDigits;
2348 AddChar(Char(UB[I]+Byte('0')));
2349 dec(I);
2350 AddChar({$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator);
2351 while O < LongInt(Width-3) do begin
2352 AddChar(Char(UB[I]+Byte('0')));
2353 dec(I);
2354 end;
2355
2356 {store exponent}
2357 AddChar('E');
2358 if Exponent < ExpBias+1 then begin
2359 AddChar('-');
2360 Exponent := ExpBias+1-Exponent;
2361 end else begin
2362 AddChar('+');
2363 dec(Exponent, ExpBias+1);
2364 end;
2365 AddChar(Char((Exponent div 10)+Byte('0')));
2366 AddChar(Char((Exponent mod 10)+Byte('0')));
2367 end;
2368
2369 function SubBcd(const B1, B2 : TBcd) : TBcd;
2370 begin
2371 Result := AddBcd(B1, NegBcd(B2));
2372 end;
2373
2374 function TruncBcd(const B : TBcd) : LongInt;
2375 var
2376 Exponent, I : Integer;
2377 Sign : Byte;
2378 UB : TUnpBcd;
2379 begin
2380 Unpack(B, UB, Exponent, Sign);
2381
2382 Result := 0;
2383 if Exponent <> 0 then begin
2384 {Bcd is not zero}
2385 I := MantissaDigits;
2386 {Add digits to left of decimal point}
2387 while (I >= 1) and (Exponent > ExpBias) do begin
2388 if Abs(Result) > MaxLongInt div 10 then
2389 {numeric overflow}
2390 RaiseBcdError(stscBcdOverflow);
2391 Result := 10*Result;
2392 if Sign <> 0 then begin
2393 if Result < -MaxLongInt-1+UB[I] then
2394 {numeric overflow}
2395 RaiseBcdError(stscBcdOverflow);
2396 dec(Result, UB[I]);
2397 end else begin
2398 if Result > MaxLongInt-UB[I] then
2399 {numeric overflow}
2400 RaiseBcdError(stscBcdOverflow);
2401 inc(Result, UB[I]);
2402 end;
2403
2404 dec(I);
2405 dec(Exponent);
2406 end;
2407 end;
2408 end;
2409
2410 function ValBcd(const S : string) : TBcd;
2411 var
2412 I, O, Digits, Exponent : Integer;
2413 Sign : Byte;
2414 ExpSigned, Rounded : Boolean;
2415 UB : TUnpBcd;
2416
2417 function SChar(I : Integer) : Char;
2418 begin
2419 if I > Length(S) then
2420 Result := #0
2421 else
2422 Result := S[I];
2423 end;
2424
2425 function IsDigit(Ch : Char) : Boolean;
2426 begin
2427 Result := (Ch >= '0') and (Ch <= '9');
2428 end;
2429
2430 procedure AddDigit(Ch : Char);
2431 begin
2432 if O > 0 then begin
2433 UB[O] := Byte(Ch)-Byte('0');
2434 dec(O);
2435 end else if not Rounded then begin
2436 {got more significant digits than will fit, must round}
2437 Rounded := True;
2438 UB[0] := Byte(Ch)-Byte('0');
2439 RoundMantissa(UB, 0);
2440 if UB[SigDigits] <> 0 then begin
2441 ShiftMantissaDown(UB, 1);
2442 inc(Digits);
2443 end;
2444 end;
2445 end;
2446
2447 begin
2448 FillChar(UB, SizeOf(TUnpBcd), 0);
2449
2450 I := 1; {input position}
2451 O := MantissaDigits; {output position}
2452 Exponent := 0;
2453 Sign := 0;
2454 Rounded := False;
2455
2456 {digits before dot, or negative digits after dot in case of 0.0000n}
2457 Digits := 0;
2458
2459 {skip leading spaces}
2460 while SChar(I) = ' ' do
2461 inc(I);
2462
2463 {get sign if any}
2464 case SChar(I) of
2465 '+' :
2466 {skip +}
2467 inc(I);
2468 '-' :
2469 begin
2470 {negative number}
2471 Sign := SignBit;
2472 inc(I);
2473 end;
2474 end;
2475
2476 {handle first digit}
2477 if SChar(I) <> {$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator then begin
2478 if not IsDigit(SChar(I)) then
2479 RaiseBcdError(stscBcdBadFormat);
2480
2481 {skip leading zeros}
2482 while SChar(I) = '0' do
2483 inc(I);
2484
2485 {add significant digits}
2486 while IsDigit(SChar(I)) do begin
2487 AddDigit(SChar(I));
2488 inc(I);
2489 inc(Digits);
2490 end;
2491 end;
2492
2493 {handle dot}
2494 if SChar(I) = {$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator then begin
2495 inc(I);
2496 if Digits = 0 then begin
2497 {no digits before dot, skip zeros after dot}
2498 while SChar(I) = '0' do begin
2499 inc(I);
2500 dec(Digits);
2501 end;
2502 end;
2503
2504 {add significant digits}
2505 while IsDigit(SChar(I)) do begin
2506 AddDigit(SChar(I));
2507 inc(I);
2508 end;
2509 end;
2510
2511 {handle exponent}
2512 case SChar(I) of
2513 'e', 'E' :
2514 begin
2515 inc(I);
2516 ExpSigned := False;
2517 case SChar(I) of
2518 '+' :
2519 {skip +}
2520 inc(I);
2521 '-' :
2522 begin
2523 {negative exponent}
2524 ExpSigned := True;
2525 inc(I);
2526 end;
2527 end;
2528 if not IsDigit(SChar(I)) then
2529 {digit must follow 'e', invalid format}
2530 RaiseBcdError(stscBcdBadFormat);
2531
2532 {collect exponent value}
2533 while IsDigit(SChar(I)) do begin
2534 Exponent := 10*Exponent+Byte(SChar(I))-Byte('0');
2535 inc(I);
2536 end;
2537
2538 if ExpSigned then
2539 Exponent := -Exponent;
2540 end;
2541 end;
2542
2543 if SChar(I) <> #0 then
2544 {should be end of string, otherwise invalid format}
2545 RaiseBcdError(stscBcdBadFormat);
2546
2547 {compute final exponent}
2548 Inc(Exponent, Digits+ExpBias);
2549
2550 if Exponent > NoSignBit then
2551 {numeric overflow}
2552 RaiseBcdError(stscBcdOverflow);
2553
2554 if (Exponent <= 0) or IsZeroMantissa(UB) then
2555 {return zero}
2556 Exponent := 0;
2557
2558 {Return packed result}
2559 Pack(UB, Exponent, Sign, Result);
2560 end;
2561
2562 function FloatFormBcd(const Mask : string; B : TBCD;
2563 const LtCurr, RtCurr : string;
2564 Sep, DecPt : Char) : string;
2565 {-Returns a formatted string with digits from B merged into the Mask}
2566 const
2567 Blank = 0;
2568 Asterisk = 1;
2569 Zero = 2;
2570 const
2571 FormChars : string = '#@*$-+,.';
2572 PlusArray : array[Boolean] of Char = ('+', '-');
2573 MinusArray : array[Boolean] of Char = (' ', '-');
2574 FillArray : array[Blank..Zero] of Char = (' ', '*', '0');
2575 var
2576 ExpB : Byte absolute B; {B's sign/exponent byte}
2577 S : string; {temporary string}
2578 Filler : integer; {char for unused digit slots: ' ', '*', '0'}
2579 WontFit, {true if number won't fit in the mask}
2580 AddMinus, {true if minus sign needs to be added}
2581 Dollar, {true if floating dollar sign is desired}
2582 Negative : Boolean; {true if B is negative}
2583 StartF, {starting point of the numeric field}
2584 EndF : Word; {end of numeric field}
2585 RtChars, {# of chars to add to right}
2586 LtChars, {# of chars to add to left}
2587 DotPos, {position of '.' in Mask}
2588 Digits, {total # of digits}
2589 Places, {# of digits after the '.'}
2590 Blanks, {# of blanks returned by StrBcd}
2591 FirstDigit, {pos. of first digit returned by Str}
2592 Extras, {# of extra digits needed for special cases}
2593 DigitPtr : Byte; {pointer into temporary string of digits}
2594 I : Word;
2595 label
2596 EndFound,
2597 RedoCase,
2598 Done;
2599 begin
2600 Result := Mask;
2601
2602 RtChars := 0;
2603 LtChars := 0;
2604
2605 {check for empty string}
2606 if Length(Mask) = 0 then
2607 goto Done;
2608
2609 {initialize variables}
2610 Filler := Blank;
2611 DotPos := 0;
2612 Places := 0;
2613 Digits := 0;
2614 Dollar := False;
2615 AddMinus := True;
2616 StartF := 1;
2617
2618 {store the sign of the real and make it positive}
2619 Negative := (ExpB and $80) <> 0;
2620 ExpB := ExpB and $7F;
2621
2622 {strip and count c's}
2623 for I := Length(Result) downto 1 do begin
2624 if Result[I] = 'C' then begin
2625 Inc(RtChars);
2626 System.Delete(Result, I, 1);
2627 end else if Result[I] = 'c' then begin
2628 Inc(LtChars);
2629 System.Delete(Result, I, 1);
2630 end;
2631 end;
2632
2633 {find the starting point for the field}
2634 while (StartF <= Length(Result)) and
2635 not CharExistsL(FormChars, Result[StartF]) do
2636 Inc(StartF);
2637 if StartF > Length(Mask) then
2638 goto Done;
2639
2640 {find the end point for the field}
2641 EndF := StartF;
2642 for I := StartF to Length(Result) do
2643 begin
2644 case Result[I] of
2645 '*' : Filler := Asterisk;
2646 '@' : Filler := Zero;
2647 '$' : Dollar := True;
2648 '-',
2649 '+' : AddMinus := False;
2650 '#' : {ignore} ;
2651 ',',
2652 '.' : DotPos := I;
2653 else
2654 goto EndFound;
2655 end;
2656 Inc(EndF);
2657 end;
2658
2659 {if we get here at all, the last char was part of the field}
2660 Inc(EndF);
2661
2662 EndFound:
2663 {if we jumped to here instead, it wasn't}
2664 Dec(EndF);
2665
2666 {disallow Dollar if Filler is Zero}
2667 if Filler = Zero then
2668 Dollar := False;
2669
2670 {we need an extra slot if Dollar is True}
2671 Extras := Ord(Dollar);
2672
2673 {get total # of digits and # after the decimal point}
2674 if EndF > Length(Result) then {!!.02}
2675 EndF := Length(Result); {!!.02}
2676
2677 for I := StartF to EndF do
2678 case Result[I] of
2679 '#', '@',
2680 '*', '$' :
2681 begin
2682 Inc(Digits);
2683 if (I > DotPos) and (DotPos <> 0) then
2684 Inc(Places);
2685 end;
2686 end;
2687
2688 {need one more 'digit' if Places > 0}
2689 Inc(Digits, Ord(Places > 0));
2690
2691 {also need an extra blank if (1) Negative is true, and (2) Filler is Blank,
2692 and (3) AddMinus is true}
2693 if Negative and AddMinus and (Filler = Blank) then
2694 Inc(Extras)
2695 else
2696 AddMinus := False;
2697
2698 {translate the BCD to a string}
2699 S := StrBCD(B, Digits, Places);
2700
2701
2702 {count number of initial blanks}
2703 Blanks := 1;
2704 while S[Blanks] = ' ' do
2705 Inc(Blanks);
2706 FirstDigit := Blanks;
2707 Dec(Blanks);
2708
2709 {the number won't fit if (a) S is longer than Digits or (b) the number of
2710 initial blanks is less than Extras}
2711 WontFit := (Length(S) > Digits) or (Blanks < Extras);
2712
2713 {if it won't fit, fill decimal slots with '*'}
2714 if WontFit then begin
2715 for I := StartF to EndF do
2716 case Result[I] of
2717 '#', '@', '*', '$' : Result[I] := '*';
2718 '+' : Result[I] := PlusArray[Negative];
2719 '-' : Result[I] := MinusArray[Negative];
2720 end;
2721 goto Done;
2722 end;
2723
2724 {fill initial blanks in S with Filler; insert floating dollar sign}
2725 if Blanks > 0 then begin
2726 StrPCopy(PChar(S), StringOfChar(FillArray[Filler], Blanks)); // FillChar(S[1], Blanks, FillArray[Filler]);
2727
2728 {put floating dollar sign in last blank slot if necessary}
2729 if Dollar then begin
2730 S[Blanks] := LtCurr[1];
2731 Dec(Blanks);
2732 end;
2733
2734 {insert a minus sign if necessary}
2735 if AddMinus then
2736 S[Blanks] := '-';
2737 end;
2738
2739 {put in the digits / signs}
2740 DigitPtr := Length(S);
2741 for I := EndF downto StartF do begin
2742 RedoCase:
2743 case Result[I] of
2744 '#', '@', '*', '$' :
2745 if DigitPtr <> 0 then begin
2746 Result[I] := S[DigitPtr];
2747 Dec(DigitPtr);
2748 if (DigitPtr <> 0) and (S[DigitPtr] = '.') then {!!.02}
2749 // if (S[DigitPtr] = '.') and (DigitPtr <> 0) then
2750 Dec(DigitPtr);
2751 end
2752 else
2753 Result[I] := FillArray[Filler];
2754 ',' : begin
2755 Result[I] := Sep;
2756 if (I < DotPos) and (DigitPtr < FirstDigit) then begin
2757 Result[I] := '#';
2758 goto RedoCase;
2759 end;
2760 end;
2761 '.' : begin
2762 Result[I] := DecPt;
2763 if (I < DotPos) and (DigitPtr < FirstDigit) then begin
2764 Result[I] := '#';
2765 goto RedoCase;
2766 end;
2767 end;
2768 '+' : Result[I] := PlusArray[Negative];
2769 '-' : Result[I] := MinusArray[Negative];
2770 end;
2771 end;
2772
2773 Done:
2774 if RtChars > 0 then begin
2775 S := RtCurr;
2776 if Length(S) > RtChars then
2777 SetLength(S, RtChars)
2778 else
2779 S := LeftPadL(S, RtChars);
2780 Result := Result + S;
2781 end;
2782
2783 if LtChars > 0 then begin
2784 S := LtCurr;
2785 if Length(S) > LtChars then
2786 SetLength(S, LtChars)
2787 else
2788 S := PadL(S, LtChars);
2789 Result := S + Result;
2790 end;
2791
2792 end;
2793
2794 {routines to support C++Builder}
2795 {$IFDEF CBuilder}
2796 procedure AddBcd_C(const B1, B2 : TBcd; var Res : TBcd);
2797 begin
2798 Res := AddBcd(B1, B2);
2799 end;
2800
2801 procedure SubBcd_C(const B1, B2 : TBcd; var Res : TBcd);
2802 begin
2803 Res := SubBcd(B1, B2);
2804 end;
2805
2806 procedure MulBcd_C(const B1, B2 : TBcd; var Res : TBcd);
2807 begin
2808 Res := MulBcd(B1, B2);
2809 end;
2810
2811 procedure DivBcd_C(const B1, B2 : TBcd; var Res : TBcd);
2812 begin
2813 Res := DivBcd(B1, B2);
2814 end;
2815
2816 procedure ModBcd_C(const B1, B2 : TBcd; var Res : TBcd);
2817 begin
2818 Res := ModBcd(B1, B2);
2819 end;
2820
2821 procedure NegBcd_C(const B : TBcd; var Res : TBcd);
2822 begin
2823 Res := NegBcd(B);
2824 end;
2825
2826 procedure AbsBcd_C(const B : TBcd; var Res : TBcd);
2827 begin
2828 Res := AbsBcd(B);
2829 end;
2830
2831 procedure FracBcd_C(const B : TBcd; var Res : TBcd);
2832 begin
2833 Res := FracBcd(B);
2834 end;
2835
2836 procedure IntBcd_C(const B : TBcd; var Res : TBcd);
2837 begin
2838 Res := IntBcd(B);
2839 end;
2840
2841 procedure RoundDigitsBcd_C(const B : TBcd; Digits : Cardinal; var Res : TBcd);
2842 begin
2843 Res := RoundDigitsBcd(B, Digits);
2844 end;
2845
2846 procedure RoundPlacesBcd_C(const B : TBcd; Places : Cardinal; var Res : TBcd);
2847 begin
2848 Res := RoundPlacesBcd(B, Places);
2849 end;
2850
2851 procedure ValBcd_C(const S : string; var Res : TBcd);
2852 begin
2853 Res := ValBcd(S);
2854 end;
2855
2856 procedure LongBcd_C(L : LongInt; var Res : TBcd);
2857 begin
2858 Res := LongBcd(L);
2859 end;
2860
2861 procedure ExtBcd_C(E : Extended; var Res : TBcd);
2862 begin
2863 Res := ExtBcd(E);
2864 end;
2865
2866 procedure ExpBcd_C(const B : TBcd; var Res : TBcd);
2867 begin
2868 Res := ExpBcd(B);
2869 end;
2870
2871 procedure LnBcd_C(const B : TBcd; var Res : TBcd);
2872 begin
2873 Res := LnBcd(B);
2874 end;
2875
2876 procedure IntPowBcd_C(const B : TBcd; E : LongInt; var Res : TBcd);
2877 begin
2878 Res := IntPowBcd(B, E);
2879 end;
2880
2881 procedure PowBcd_C(const B, E : TBcd; var Res : TBcd);
2882 begin
2883 Res := PowBcd(B, E);
2884 end;
2885
2886 procedure SqrtBcd_C(const B : TBcd; var Res : TBcd);
2887 begin
2888 Res := SqrtBcd(B);
2889 end;
2890 {$ENDIF}
2891
2892 initialization
2893 ZeroBcd := FastVal('0.0');
2894 MinBcd := ValBcd('-9'+{$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator+'9E+63');
2895 BadBcd := MinBcd;
2896 MaxBcd := ValBcd('9'+{$IFDEF DELPHIXE2}FormatSettings.{$ENDIF}DecimalSeparator+'9E+63');
2897 PiBcd := FastVal('3.1415926535897932384626433832795028841971');
2898 Ln10Bcd := FastVal('2.3025850929940456840179914546843642076011');
2899 eBcd := FastVal('2.7182818284590452353602874713526624977572');
2900 end.

  ViewVC Help
Powered by ViewVC 1.1.20