ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/branches/udr/client/FBNumeric.pas
Revision: 371
Committed: Wed Jan 5 15:21:22 2022 UTC (2 years, 10 months ago) by tony
Content type: text/x-pascal
File size: 11581 byte(s)
Log Message:
Beta Release 0.1

File Contents

# Content
1 (*
2 * Firebird Interface (fbintf). The fbintf components provide a set of
3 * Pascal language bindings for the Firebird API. Although predominantly
4 * a new development they include source code taken from IBX and may be
5 * considered a derived product. This software thus also includes the copyright
6 * notice and license conditions from IBX.
7 *
8 * Except for those parts dervied from IBX, contents of this file are subject
9 * to the Initial Developer's Public License Version 1.0 (the "License"); you
10 * may not use this file except in compliance with the License. You may obtain a
11 * copy of the License here:
12 *
13 * http://www.firebirdsql.org/index.php?op=doc&id=idpl
14 *
15 * Software distributed under the License is distributed on an "AS
16 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
17 * implied. See the License for the specific language governing rights
18 * and limitations under the License.
19 *
20 * The Initial Developer of the Original Code is Tony Whyman.
21 *
22 * The Original Code is (C) 2021 Tony Whyman, MWA Software
23 * (http://www.mwasoftware.co.uk).
24 *
25 * All Rights Reserved.
26 *
27 * Contributor(s): ______________________________________.
28 *
29 *)
30 unit FBNumeric;
31
32 {$IFDEF MSWINDOWS}
33 {$DEFINE WINDOWS}
34 {$ENDIF}
35
36 {$IFDEF FPC}
37 {$mode delphi}
38 {$codepage UTF8}
39 {$interfaces COM}
40 {$ENDIF}
41
42 interface
43
44 uses
45 Classes, SysUtils, IB, FBActivityMonitor, FmtBCD;
46
47 { The IFBNumeric interface is a managed type to hold a fixed point integer
48 as a 64-bit signed integer plus a scale factor. i.e. the scale factor is
49 the base 10 exponent in
50
51 Fixed Point Value = <64-bit signed integer> * 10^<scale factor>
52
53 }
54
55 function NewNumeric(aValue: AnsiString): IFBNumeric; overload;
56 function NewNumeric(aValue: double; aScale: integer): IFBNumeric; overload;
57 function NewNumeric(aValue: TBCD): IFBNumeric; overload;
58 function NewNumeric(aValue: currency): IFBNumeric; overload;
59 function NewNumeric(aValue: Int64): IFBNumeric; overload;
60 function NumericFromRawValues(aValue: Int64; aScale: integer): IFBNumeric;
61
62 function TryStrToNumeric(S: Ansistring; out Value: int64; out scale: integer): boolean;
63 function NumericToDouble(aValue: Int64; aScale: integer): double;
64
65 function SafeSmallInt(aValue: Int64): Smallint;
66 function SafeInteger(aValue: Int64): integer;
67
68
69 implementation
70
71 uses IBUtils, FBMessages, Math;
72
73 type
74
75 { TIBNumeric }
76
77 { TFBNumeric }
78
79 TFBNumeric = class(TFBInterfacedObject,IFBNumeric)
80 private
81 FValue: Int64;
82 FScale: integer;
83 // function AdjustScaleFromCurrency(Value: Currency; aScale: Integer): Int64;
84 public
85 constructor Create(aValue: Int64; aScale: integer); overload;
86 constructor Create(aValue: Int64); overload;
87 constructor Create(aValue: AnsiString); overload;
88 constructor Create(aValue: double; aScale: integer); overload;
89 constructor Create(aValue: Currency); overload;
90 constructor Create(aValue: TBCD); overload;
91 public
92 {IFBNumeric}
93 function getRawValue: Int64;
94 function getScale: integer;
95 function clone(aNewScale: integer): IFBNumeric;
96 function getAsString: AnsiString;
97 function getAsDouble: double;
98 function getAsCurrency: Currency;
99 function getAsBCD: TBCD;
100 function getAsInt64: Int64; {scaled}
101 function getAsInteger: integer; {scaled - may be truncated}
102 function getAsSmallInt: SmallInt; {scaled - may be truncated}
103 end;
104
105 function NewNumeric(aValue: AnsiString): IFBNumeric;
106 begin
107 Result := TFBNumeric.Create(aValue);
108 end;
109
110 function NewNumeric(aValue: double; aScale: integer): IFBNumeric;
111 begin
112 Result := TFBNumeric.Create(aValue,aScale);
113 end;
114
115 function NewNumeric(aValue: TBCD): IFBNumeric;
116 begin
117 Result := TFBNumeric.Create(aValue);
118 end;
119
120 function NewNumeric(aValue: currency): IFBNumeric;
121 begin
122 Result := TFBNumeric.Create(aValue);
123 end;
124
125 function NewNumeric(aValue: Int64): IFBNumeric;
126 begin
127 Result := TFBNumeric.Create(aValue);
128 end;
129
130 function NumericFromRawValues(aValue: Int64; aScale: integer): IFBNumeric;
131 begin
132 Result := TFBNumeric.Create(aValue,aScale);
133 end;
134
135 function TryStrToNumeric(S: Ansistring; out Value: int64; out scale: integer): boolean;
136 var i: integer;
137 ds: integer;
138 exponent: integer;
139 begin
140 Result := false;
141 ds := 0;
142 exponent := 0;
143 S := Trim(S);
144 Value := 0;
145 scale := 0;
146 if Length(S) = 0 then
147 Exit;
148 {$IF declared(DefaultFormatSettings)}
149 with DefaultFormatSettings do
150 {$ELSE}
151 {$IF declared(FormatSettings)}
152 with FormatSettings do
153 {$IFEND}
154 {$IFEND}
155 begin
156 for i := length(S) downto 1 do
157 begin
158 if S[i] = AnsiChar(DecimalSeparator) then
159 begin
160 if ds <> 0 then Exit; {only one allowed}
161 ds := i;
162 dec(exponent);
163 system.Delete(S,i,1);
164 end
165 else
166 if S[i] in ['+','-'] then
167 begin
168 if (i > 1) and not (S[i-1] in ['e','E']) then
169 Exit; {malformed}
170 end
171 else
172 if S[i] in ['e','E'] then {scientific notation}
173 begin
174 if ds <> 0 then Exit; {not permitted in exponent}
175 if exponent <> 0 then Exit; {only one allowed}
176 exponent := i;
177 end
178 else
179 if not (S[i] in ['0'..'9']) then
180 {Note: ThousandSeparator not allowed by Delphi specs}
181 Exit; {bad character}
182 end;
183
184 if exponent > 0 then
185 begin
186 Result := TryStrToInt(system.copy(S,exponent+1,maxint),Scale);
187 if Result then
188 begin
189 {adjust scale for decimal point}
190 if ds <> 0 then
191 Scale := Scale - (exponent - ds);
192 Result := TryStrToInt64(system.copy(S,1,exponent-1),Value);
193 end;
194 end
195 else
196 begin
197 if ds > 0 then
198 begin
199 {remove trailing zeroes}
200 while (ds < length(S)) and (S[length(S)] = '0') do {no need to check length as ds > 0}
201 system.delete(S,Length(S),1);
202 scale := ds - Length(S) - 1;
203 end;
204 Result := TryStrToInt64(S,Value);
205 end;
206 end;
207 end;
208
209 function NumericToDouble(aValue: Int64; aScale: integer): double;
210 begin
211 Result := aValue * IntPower(10,aScale);
212 end;
213
214 function SafeSmallInt(aValue: Int64): Smallint;
215 begin
216 if aValue > High(smallint) then
217 IBError(ibxeIntegerOverflow,[]);
218 if aValue < Low(smallint) then
219 IBError(ibxIntegerUnderflow,[]);
220 Result := aValue;
221 end;
222
223 function SafeInteger(aValue: Int64): integer;
224 begin
225 if aValue > High(integer) then
226 IBError(ibxeIntegerOverflow,[]);
227 if aValue < Low(integer) then
228 IBError(ibxIntegerUnderflow,[]);
229 Result := aValue;
230 end;
231
232 { TFBNumeric }
233
234 (*function TFBNumeric.AdjustScaleFromCurrency(Value: Currency; aScale: Integer
235 ): Int64;
236 var
237 Scaling : Int64;
238 i : Integer;
239 begin
240 Result := 0;
241 Scaling := 1;
242 if aScale < 0 then
243 begin
244 for i := -1 downto aScale do
245 Scaling := Scaling * 10;
246 result := trunc(Value * Scaling);
247 end
248 else
249 if aScale > 0 then
250 begin
251 for i := 1 to aScale do
252 Scaling := Scaling * 10;
253 result := trunc(Value / Scaling);
254 end
255 else
256 result := trunc(Value);
257 end;*)
258
259 constructor TFBNumeric.Create(aValue: Int64; aScale: integer);
260 begin
261 inherited Create;
262 FValue := aValue;
263 FScale := aScale;
264 end;
265
266 constructor TFBNumeric.Create(aValue: Int64);
267 begin
268 inherited Create;
269 FValue := aValue;
270 FScale := 0;
271 end;
272
273 constructor TFBNumeric.Create(aValue: AnsiString);
274 begin
275 inherited Create;
276 if not TryStrToNumeric(aValue,FValue,FScale) then
277 IBError(ibxeInvalidDataConversion,[aValue]);
278 end;
279
280 constructor TFBNumeric.Create(aValue: double; aScale: integer);
281 var
282 Scaling : Int64;
283 i : Integer;
284 begin
285 inherited Create;
286 FScale := aScale;
287 FValue := 0;
288 Scaling := 1;
289 if aScale < 0 then
290 begin
291 for i := -1 downto aScale do
292 Scaling := Scaling * 10;
293 FValue := trunc(aValue * Scaling);
294 end
295 else
296 if aScale > 0 then
297 begin
298 for i := 1 to aScale do
299 Scaling := Scaling * 10;
300 FValue := trunc(aValue / Scaling);
301 end
302 else
303 FValue := trunc(aValue);
304 // writeln('Adjusted ',Value,' to ',Result);
305 end;
306
307 constructor TFBNumeric.Create(aValue: Currency);
308 begin
309 inherited Create;
310 Move(aValue,FValue,sizeof(Int64));
311 FScale := -4;
312 end;
313
314 constructor TFBNumeric.Create(aValue: TBCD);
315 var ScaledBCD: TBCD;
316 begin
317 inherited Create;
318 FScale := -BCDScale(aValue);
319 BCDMultiply(aValue,Power(10,-FScale),ScaledBCD);
320 FValue := BCDToInteger(ScaledBCD,true);
321 end;
322
323 function TFBNumeric.getRawValue: Int64;
324 begin
325 Result := FValue;
326 end;
327
328 function TFBNumeric.getScale: integer;
329 begin
330 Result := FScale;
331 end;
332
333 function TFBNumeric.clone(aNewScale: integer): IFBNumeric;
334 begin
335 Result := TFBNumeric.Create(Round(FValue * IntPower(10,FScale-aNewScale)),aNewScale);
336 end;
337
338 function TFBNumeric.getAsString: AnsiString;
339 var Scaling : AnsiString;
340 i: Integer;
341 begin
342 Result := IntToStr(FValue);
343 Scaling := '';
344 if FScale > 0 then
345 begin
346 for i := 1 to FScale do
347 Result := Result + '0';
348 end
349 else
350 if FScale < 0 then
351 {$IF declared(DefaultFormatSettings)}
352 with DefaultFormatSettings do
353 {$ELSE}
354 {$IF declared(FormatSettings)}
355 with FormatSettings do
356 {$IFEND}
357 {$IFEND}
358 begin
359 if Length(Result) > -FScale then
360 system.Insert(DecimalSeparator,Result,Length(Result) + FScale+1)
361 else
362 begin
363 Scaling := '0' + DecimalSeparator;
364 for i := -1 downto FScale + Length(Result) do
365 Scaling := Scaling + '0';
366 if FValue < 0 then
367 system.insert(Scaling,Result,2)
368 else
369 Result := Scaling + Result;
370 end;
371 end;
372 end;
373
374 function TFBNumeric.getAsDouble: double;
375 begin
376 Result := NumericToDouble(FValue,FScale);
377 end;
378
379 function TFBNumeric.getAsCurrency: Currency;
380 var value: int64;
381 begin
382 if FScale <> -4 then
383 begin
384 value := clone(-4).GetRawValue;
385 Move(value,Result,sizeof(Currency));
386 end
387 else
388 Move(FValue,Result,sizeof(Currency));
389 end;
390
391 (*var
392 Scaling : Int64;
393 i : Integer;
394 FractionText, PadText, CurrText: AnsiString;
395 begin
396 Result := 0;
397 Scaling := 1;
398 PadText := '';
399 if FScale > 0 then
400 begin
401 for i := 1 to FScale do
402 Scaling := Scaling * 10;
403 result := FValue * Scaling;
404 end
405 else
406 if FScale < 0 then
407 begin
408 for i := -1 downto FScale do
409 Scaling := Scaling * 10;
410 FractionText := IntToStr(abs(FValue mod Scaling));
411 for i := Length(FractionText) to -FScale -1 do
412 PadText := '0' + PadText;
413 {$IF declared(DefaultFormatSettings)}
414 with DefaultFormatSettings do
415 {$ELSE}
416 {$IF declared(FormatSettings)}
417 with FormatSettings do
418 {$IFEND}
419 {$IFEND}
420 if FValue < 0 then
421 CurrText := '-' + IntToStr(Abs(FValue div Scaling)) + DecimalSeparator + PadText + FractionText
422 else
423 CurrText := IntToStr(Abs(FValue div Scaling)) + DecimalSeparator + PadText + FractionText;
424 try
425 result := StrToCurr(CurrText);
426 except
427 on E: Exception do
428 IBError(ibxeInvalidDataConversion, [nil]);
429 end;
430 end
431 else
432 result := FValue;
433 end; *)
434
435 function TFBNumeric.getAsBCD: TBCD;
436 begin
437 Result := DoubleToBCD(getAsDouble);
438 end;
439
440 function TFBNumeric.getAsInt64: Int64;
441 var
442 Scaling : Int64;
443 i: Integer;
444 Val: Int64;
445 begin
446 Scaling := 1;
447 Val := FValue;
448 if FScale > 0 then begin
449 for i := 1 to FScale do Scaling := Scaling * 10;
450 result := Val * Scaling;
451 end else if FScale < 0 then begin
452 for i := -1 downto FScale do Scaling := Scaling * 10;
453 result := Val div Scaling;
454 end else
455 result := Val;
456 end;
457
458 function TFBNumeric.getAsInteger: integer;
459 begin
460 Result := SafeInteger(getAsInt64);
461 end;
462
463 function TFBNumeric.getAsSmallInt: SmallInt;
464 begin
465 Result := SafeSmallInt(getAsInt64);
466 end;
467
468 end.
469