Sign in Join
Wiki copytemporary

BcdUnit: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Welcome
22
33Welcome to information page about FMTBcd. Goal of this page is to contain information needed for implementing FPC version of the FMTBcd.
44
55------------------------------------------------------------------------
66
77FMTBcd unit defines a number of binary-coded decimal (BCD) routines:
88
99## Types
1010
1111``` pascal
1212TBcd = packed record
1313 Precision: Byte;
1414 SignSpecialPlaces: Byte;
1515 Fraction: packed array [0..31] of Byte;
1616 end;
1717```
1818
19-```
19+```text
2020PBcd: pointer to TBcd
2121```
2222
2323### Precision
2424
2525Precision states how many decimal digits are valid out of the 64 nibbles available for use.
2626
2727### SignSpecialPlaces
2828
2929SignSpecialPlaces is three fields packed into a byte.
3030
3131- The first bit is the sign (bit value 128).
3232- The second bit is 'Special' (So special, I can't work out what it's for).
3333- The third - eights bits contain the number of decimals (less the number of default digits???). 6 bits gives us up to 32 digits.
3434
3535### Fraction
3636
3737array of BCD Nibbles, 00..99 per Byte, high Nibble 1st
3838
3939Can anybody correct the following test cases?
4040
4141
4242
4343What do you mean by 'correct'?
4444
4545## test cases
4646
4747### integers
4848
4949If you look at a Tbdc record in the debugger, it will display the Precision as an integer, the SignSpecialPlaces as an interger and the Fraction as an array of integers. The fraction parts of the TBCD are really arrays of nibbles - one nibble per digit, two per byte. Following are some 'debug displays' and their translations. There is a space between bytes. So...
5050
5151zero is boring...
5252
53-```
53+```text
5454 0 -> (Precision: 0;SignSpecialPlaces: 0; Fraction:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
5555```
5656
57571..3
5858
59-```
59+```text
6060 1 -> (Precision: 1;SignSpecialPlaces: 0; Fraction:(16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
6161 1 -> (Precision: 1;SignSpecialPlaces: 0; Fraction:(1,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
6262```
6363
64-```
64+```text
6565 2 -> (Precision: 1;SignSpecialPlaces: 0; Fraction:(32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
6666 2 -> (Precision: 1;SignSpecialPlaces: 0; Fraction:(2,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
6767```
6868
69-```
69+```text
7070 3 -> (Precision: 1;SignSpecialPlaces: 0; Fraction:(48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
7171 3 -> (Precision: 1;SignSpecialPlaces: 0; Fraction:(3,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
7272```
7373
74-```
74+```text
7575etc...
7676 4 -> (Precision: 1;SignSpecialPlaces: 0; Fraction:(64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
7777 5 -> (Precision: 1;SignSpecialPlaces: 0; Fraction:(80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
7878 6 -> (Precision: 1;SignSpecialPlaces: 0; Fraction:(96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
7979 7 -> (Precision: 1;SignSpecialPlaces: 0; Fraction:(112,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
8080 8 -> (Precision: 1;SignSpecialPlaces: 0; Fraction:(128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
8181 9 -> (Precision: 1;SignSpecialPlaces: 0; Fraction:(144,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
8282```
8383
848410 - note: the fractional part is the same as for 1, the precision is now 2. i.e. 10 = 1 * 10^2
8585
86-```
86+```text
8787 10 -> (Precision: 2;SignSpecialPlaces: 0; Fraction:(16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
8888 10 -> (Precision: 2;SignSpecialPlaces: 0; Fraction:(1,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
8989```
9090
9191100 - note: the fractional part is the same as for 1, the precision is now 3. i.e. 10 = 1 * 10^3
9292
93-```
93+```text
9494 100 -> (Precision: 3;SignSpecialPlaces: 0; Fraction:(16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
9595 100 -> (Precision: 2;SignSpecialPlaces: 0; Fraction:(1,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
9696etc
9797 1000 -> (Precision: 4;SignSpecialPlaces: 0; Fraction:(16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
9898 1000 -> (Precision: 4;SignSpecialPlaces: 0; Fraction:(1,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
9999```
100100
101101some more interesting cases:
102102
103-```
103+```text
104104 1111 -> (Precision: 4;SignSpecialPlaces: 0; Fraction:(17,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
105105 1111 -> (Precision: 4;SignSpecialPlaces: 0; Fraction:(1,1, 1,1, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
106106```
107107
108-```
108+```text
109109 1234 -> (Precision: 4;SignSpecialPlaces: 0; Fraction:(18,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
110110 1234 -> (Precision: 4;SignSpecialPlaces: 0; Fraction:(1,2, 3,4, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
111111```
112112
113-```
113+```text
114114 -5 -> (Precision: 1;SignSpecialPlaces: 128;Fraction:(80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
115115 -5 -> (Precision: 1;SignSpecialPlaces: 128;Fraction:(5,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
116116```
117117
118-```
118+```text
119119 -6 -> (Precision: 1;SignSpecialPlaces: 128;Fraction:(96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
120120 -6 -> (Precision: 1;SignSpecialPlaces: 128;Fraction:(6,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
121121```
122122
123-```
123+```text
124124 9999 -> (Precision: 4;SignSpecialPlaces: 0; Fraction:(153,153,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
125125 9999 -> (Precision: 4;SignSpecialPlaces: 0; Fraction:(9,9, 9,9, 9,9, 9,9, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
126126```
127127
128-```
128+```text
129129 -9999 -> (Precision: 4;SignSpecialPlaces: 128;Fraction:(153,153,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
130130 -9999 -> (Precision: 4;SignSpecialPlaces: 128; Fraction:(9,9, 9,9, 9,9, 9,9, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
131131```
132132
133133Displaying the structure in HEX helps...(you should never see any hex digits in the fraction part - only 0..9 is used)
134134
135-```
135+```text
136136 32767 -> (Precision: 5;SignSpecialPlaces: 0; Fraction:($32,$76,$70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
137137 32767 -> (Precision: 5;SignSpecialPlaces: 0; Fraction:(3,2, 7,6, 70,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
138138```
139139
140-```
140+```text
141141 -32768 -> (Precision: 5;SignSpecialPlaces: 128;Fraction:($32,$76,$80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
142142 -32768 -> (Precision: 5;SignSpecialPlaces: 128;Fraction:(3,2, 7,6, 80,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
143143```
144144
145-```
145+```text
146146 2147483647 -> (Precision:10;SignSpecialPlaces: 0; Fraction:($21,$47,$48,$36,$47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
147147 2147483647 -> (Precision:10;SignSpecialPlaces: 0; Fraction:(2,1, 4,7, 4,8, 3,6, 4,7, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
148148```
149149
150-```
150+```text
151151 -2147483647 -> (Precision:10;SignSpecialPlaces: 128;Fraction:($21,$47,$48,$36,$47,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0))
152152 -2147483647 -> (Precision:10;SignSpecialPlaces: 128;Fraction:(2,1, 4,7, 4,8, 3,6, 4,7, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,...
153153```
154154
155155### floats
156156
157-```
157+```text
158158 -0.01234567 -> (Precision:8; SignSpecialPlaces: 136 {$80+8} Fraction: ( 1,35,69,103, ... 0,0)
159159```
160160
161161## Const
162162
163-```
163+```text
164164 NullBcd: TBcd = (Precision: 0; SignSpecialPlaces: 0; Fraction:(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0));
165165```
166166
167167it's a zero (according to Delphi documentation: "Use NullBcd to indicate an unknown or missing TBcd value. NullBcd is a TBcd with all its fields set to 0. ")
168168
169169## Exceptions
170170
171-```
171+```text
172172 EBcdException, EBcdOverflowException
173173```
174174
175175??
176176
177-```
177+```text
178178 EBcdException = Class(Exception);
179179 EBcdOverflowException = Class(Exception);
180180```
181181
182182## Utility
183183
184184``` pascal
185185 function BcdPrecision(const Bcd: TBcd): Word;
186186 function BcdScale(const Bcd: TBcd): Word;
187187 function IsBcdNegative(const Bcd: TBcd): Boolean;
188188```
189189
190190??
191191
192192## Arithmetic
193193
194194``` pascal
195195 procedure BcdAdd(const bcdIn1, bcdIn2: TBcd; var bcdOut: TBcd);
196196 procedure BcdSubtract(const bcdIn1, bcdIn2: TBcd; var bcdOut: TBcd);
197197 function NormalizeBcd(const InBcd: TBcd; var OutBcd: TBcd; const Prec, Scale: Word): Boolean;
198198```
199199
200200Returns True if successful, False if Int Digits needed to be truncated
201201
202202``` pascal
203203 procedure BcdMultiply(const bcdIn1, bcdIn2: TBcd; var bcdOut: TBcd); overload;
204204 procedure BcdMultiply(const bcdIn: TBcd; const DoubleIn: Double; var bcdOut: TBcd); overload;
205205 procedure BcdMultiply(const bcdIn: TBcd; const StringIn: string; var bcdOut: TBcd); overload;
206206 procedure BcdMultiply(StringIn1, StringIn2: string; var bcdOut: TBcd); overload;
207207 procedure BcdDivide(Dividend, Divisor: string; var bcdOut: TBcd); overload;
208208 procedure BcdDivide(const Dividend, Divisor: TBcd; var bcdOut: TBcd); overload;
209209 procedure BcdDivide(const Dividend: TBcd; const Divisor: Double; var bcdOut: TBcd); overload;
210210 procedure BcdDivide(const Dividend: TBcd; const Divisor: string; var bcdOut: TBcd); overload;
211211```
212212
213213## Creation
214214
215215``` pascal
216216 procedure VarFMTBcdCreate(var ADest: Variant; const ABcd: TBcd); overload;
217217 function VarFMTBcdCreate: Variant; overload;
218218 function VarFMTBcdCreate(const AValue: string; Precision, Scale: Word):Variant; overload;
219219 function VarFMTBcdCreate(const AValue: Double; Precision: Word; Scale: Word ): Variant; overload;
220220 function VarFMTBcdCreate(const ABcd: TBcd): Variant; overload;
221221 function VarIsFMTBcd(const AValue: Variant): Boolean; overload;
222222 function VarFMTBcd: TVarType;
223223```
224224
225225## Conversions
226226
227-```
227+```text
228228 StrToBcd, TryStrToBcd, DoubleToBcd, DoubleToBcd, IntegerToBcd,
229229 VarToBcd, CurrToBCD, BcdToStr, BcdToDouble, BcdToInteger,
230230 BCDToCurr, BcdToStrF, FormatBcd, BcdCompare
231231```
232232
233233### to BCD
234234
235235Convert String/Double/Integer to BCD struct
236236
237237``` pascal
238238 function StrToBcd(const AValue: string): TBcd;
239239 function TryStrToBcd(const AValue: string; var Bcd: TBcd): Boolean;
240240 function DoubleToBcd(const AValue: Double): TBcd; overload;
241241 procedure DoubleToBcd(const AValue: Double; var bcd: TBcd); overload;
242242 function IntegerToBcd(const AValue: Longint): TBcd;
243243 function VarToBcd(const AValue: Variant): TBcd;
244244 function CurrToBCD(const Curr: Currency; var BCD: TBcd; Precision: Integer; Decimals: Integer): Boolean;
245245```
246246
247247### from BCD
248248
249249Convert Bcd struct to string/Double/Integer
250250
251251``` pascal
252252 function BcdToStr(const Bcd: TBcd): string; overload;
253253 function BcdToDouble(const Bcd: TBcd): Double;
254254 function BcdToInteger(const Bcd: TBcd; Truncate: Boolean): Longint;
255255 function BCDToCurr(const BCD: TBcd; var Curr: Currency): Boolean;
256256 function BcdToStrF(const Bcd: TBcd; Format: TFloatFormat; const Precision, Digits: Integer): string;
257257```
258258
259259Formatting Bcd as string
260260
261261``` pascal
262262 function FormatBcd(const Format: string; Bcd: TBcd): string;
263263 function BcdCompare(const bcd1, bcd2: TBcd): Integer;
264264```
265265
266266Warnings: 1. All these functions can accept integer as a parameter, but they produce a wrong result. For converting integers, use
267267
268268``` pascal
269269 function BCDToInt(Value: Integer):Integer;
270270```
271271
272272from the *sysutils* unit.
273273
2742742\. Converting invalid BCD arrays cause exceptions.
275275
276276## Resources
277277
278278- [Additional information](http://homepages.borland.com/efg2lab/Library/Delphi/MathInfo/) available from efg's Computer Lab ([archived](https://web.archive.org/web/20080208085333/http://homepages.borland.com/efg2lab/Library/Delphi/MathInfo/) at archive.org).
279279
280280---
281281*From the Free Pascal wiki, page "BcdUnit", as it stood on 2021-02-17 after a change by Bart, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
282282Category: FPC
283283Category: Proposals

Back to history