MathgeomGLS
2521 строка · 96.0 Кб
1{---------------------------------------------------------------------------}
2{ }
3{ File: Velthuis.BigDecimals.pas }
4{ HFunction: A multiple precision decimal implementation, based on the }
5{ BigInteger implementation in Velthuis.BigIntegers.pas. }
6{ Language: Delphi version XE2 or later }
7{ Author: Rudy Velthuis }
8{ Copyright: (c) 2016,2017 Rudy Velthuis }
9{ ------------------------------------------------------------------------- }
10{ }
11{ License: Redistribution and use in source and binary forms, with or }
12{ without modification, are permitted provided that the }
13{ following conditions are met: }
14{ }
15{ * Redistributions of source code must retain the above }
16{ copyright notice, this list of conditions and the following }
17{ disclaimer. }
18{ * Redistributions in binary form must reproduce the above }
19{ copyright notice, this list of conditions and the following }
20{ disclaimer in the documentation and/or other materials }
21{ provided with the distribution. }
22{ }
23{ Disclaimer: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" }
24{ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT }
25{ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND }
26{ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO }
27{ EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE }
28{ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, }
29{ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
30{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, }
31{ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED }
32{ AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT }
33{ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) }
34{ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF }
35{ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. }
36{ }
37{---------------------------------------------------------------------------}
38
39{---------------------------------------------------------------------------}
40{ }
41{ Notes: The interface of BigDecimal is mainly the same as the Java }
42{ BigDecimal type. But because Java does not have operator }
43{ overloading, it uses methods for all the arithmetic }
44{ operations, so it is not a big problem to give these extra }
45{ MathContext parameters, e.g for division or conversions. }
46{ Division might result in a non-terminating expansion (e.g. }
47{ 1 / 3 does not terminate, so there must be a given limit on }
48{ the number of digits. This generally requires rounding). }
49{ }
50{ To be able to use overloaded operators, I decided to give }
51{ the class DefaultPrecision and DefaultRoundingMode }
52{ properties, to be used in any division or conversion that }
53{ requires these. Additionally, there are methods that take }
54{ Precision or RoundingMode parameters, which do not use the }
55{ defaults. }
56{ }
57{ The names of some of the methods and identifiers were chosen }
58{ to be more in line with BigInteger and with other Delphi }
59{ functions and constants. }
60{ }
61{ The ToString and ToPlainString methods do follow the Java }
62{ interface, i.e. ToString produces scientific notation where }
63{ this makes sense while ToPlainString produces plain notation, }
64{ even if this means that the string can get very long and }
65{ contains many zeroes. Both ToString and ToPlainString do not }
66{ use local format settings, but use the system invariant }
67{ format settings instead. This allows the output of these }
68{ methods to be used as valid input for Parse and TryParse (so }
69{ called roundtrip conversion). }
70{ If you want output based on FormatSettings, use my upcoming }
71{ NumberFormatter instead. }
72{ }
73{ Based on the Java examples and on my Decimal types, I use a }
74{ Scale which is positive for fractions, even if a positive }
75{ exponent might make more sense. }
76{ }
77{ BigDecimals are immutable. This means that if a method }
78{ returns a value that differs from the value of the current }
79{ BigDecimal, a new BigDecimal is returned. }
80{ }
81{---------------------------------------------------------------------------}
82
83unit Velthuis.BigDecimals;
84
85(* TODO: BigDecimals are ssslllooowww. This piece of code, with CIterations = 5*1000*1000,
86
87SetLength(arr, CIterations);
88pi := '3.14159';
89for I := 0 to High(arr) do
90arr[I] := BigDecimal(I);
91for I := 0 to High(arr) do
92arr[I] := arr[I] * pi / (pi * BigDecimal(I) + BigDecimal.One);
93
94is 500 times(!) slower than the Double equivalent. That is extremely slow.
95Note that simplyfying this to do BigDecimal(I) * pi only once will only take away 1% of that.
96It is crucial to find out what makes this code so terribly slow.
97
98Note: probably BigInteger.DivMod is the slow part.
99
100Note: It might make sense to use a NativeInt to hold the FValue of small BigDecimals, instead
101of always BigIntegers.
102It might also make sense to make BigInteger.DivMod a lot faster for small values.
103*)
104
105interface
106
107uses
108CompilerAndRTLVersions,
109System.SysUtils,
110System.Math,
111Velthuis.BigIntegers;
112
113{$IF CompilerVersion >= CompilerVersionDelphi2010} // Delphi 2010
114{$DEFINE HasClassConstructors}
115{$IFEND}
116
117{$IF CompilerVersion >= CompilerVersionDelphiXE}
118{$CODEALIGN 16}
119{$ALIGN 16}
120{$IFEND}
121
122{$IF CompilerVersion >= CompilerVersionDelphiXE3}
123{$LEGACYIFEND ON}
124{$IFEND}
125
126{$IF CompilerVersion < CompilerVersionDelphiXE8}
127{$IF (DEFINED(WIN32) or DEFINED(CPUX86)) AND NOT DEFINED(CPU32BITS)}
128{$DEFINE CPU32BITS}
129{$IFEND}
130{$IF (DEFINED(WIN64) OR DEFINED(CPUX64)) AND NOT DEFINED(CPU64BITS)}
131{$DEFINE CPU64BITS}
132{$IFEND}
133{$IFEND}
134
135{$IF SizeOf(Extended) > SizeOf(Double)}
136{$DEFINE HasExtended}
137{$IFEND}
138
139{$DEFINE EXPERIMENTAL}
140
141
142type
143// Note: where possible, existing exception types are used, e.g. EConvertError, EOverflow, EUnderflow,
144// EZeroDivide from System.SysUtils, etc.
145
146/// <summary>This exception is raised when on rounding, rmUnnecessary is specified, indicating that we
147/// "know" that rounding is not necessary, and the code determines that, to get the desired result, rounding is
148/// necessary after all.</summary>
149ERoundingNecessary = class(Exception);
150EIntPowerExponent = class(Exception);
151
152PBigDecimal = ^BigDecimal;
153
154/// <summary>BigDecimal is a multiple precision floating decimal point binary significand data type. It consists
155/// of a BigInteger and a scale, which is the negative decimal exponent.</summary>
156/// <remarks><para>BigDecimal "remembers" the precision with which it was initialized. So BigDecimal('1.79') and
157/// BigDecimal('1.790000') are distinct values, although they compare as equal.</para>
158/// <para>BigDecimals are immutable. This means that any function or operator that returns a different
159/// value returns a new BigDecimal.</para></remarks>
160BigDecimal = record
161
162public
163/// <summary>RoundingMode governs which rounding mode is used for certain operations, like division or
164/// conversion.</summary>
165/// <param name="rmUp">Rounds away from zero</param>
166/// <param name="rmDown">Rounds towards zero</param>
167/// <param name="rmCeiling">Rounds towards +infinity</param>
168/// <param name="rmFloor">Rounds towards -infinity</param>
169/// <param name="rmNearestUp">Rounds to nearest higher order digit and, on tie, away from zero</param>
170/// <param name="rmNearestDown">Rounds to nearest higher order digit and, on tie, towards zero</param>
171/// <param name="rmNearestEven">Rounds to nearest higher order digit and, on tie, to nearest even digit</param>
172/// <param name="rmUnnecessary">Assumes an exact result, and raises an exception if rounding is necessary</param>
173type
174RoundingMode =
175(
176rmUp, // Round away from zero
177rmDown, // Round towards zero
178rmCeiling, // Round towards +infinity
179rmFloor, // Round towards -infinity
180rmNearestUp, // Round .5 away from 0
181rmNearestDown, // Round .5 towards 0
182rmNearestEven, // Round .5 towards the nearest even value
183rmUnnecessary // Do not round, because operation has exact result
184);
185
186const
187/// <summary>Maximum value a BigDecimal's scale can have</summary>
188MaxScale = MaxInt div SizeOf(Velthuis.BigIntegers.TLimb);
189
190/// <summary>Minimum value a BigDecimal's scale can have</summary>
191MinScale = -MaxScale - 1;
192
193{$IF defined(CPU32BITS)}
194IntPowerExponentThreshold = 128;
195{$ELSE}
196IntPowerExponentThreshold = 256;
197{$IFEND}
198
199private
200type
201// Error codes to be used when calling the private static BigDecimal.Error method.
202TErrorCode = (ecParse, ecDivByZero, ecConversion, ecOverflow, ecUnderflow, ecInvalidArg, ecRounding, ecExponent);
203
204var
205// The unscaled value of the BigDecimal.
206FValue: BigInteger;
207
208// The scale which is the power of ten by which the UnscaledValue must be divided to get the BigDecimal value.
209// So 1.79 is coded as FValue = 179 and FScale = 2, whereas 1.7900 is coded as FValue = 17900 and FScale = 4.
210FScale: Int32;
211
212// The precision is the number of digits in FValue. This is originally 0, and calculated when used the first time.
213// If this value is not 0, then the precision does not need to be calculated and this value can be used.
214FPrecision: Int32;
215
216class var
217// Default rounding mode. See above.
218FDefaultRoundingMode: RoundingMode;
219
220// Default precision (number of significant digits) used for e.g. division.
221FDefaultPrecision: Integer;
222
223// Set this to False if trailing zeroes should not be reduced to the preferred scale after a division.
224FReduceTrailingZeros: Boolean;
225
226// Default character used to indicate exponent in scientific notation output. Either 'E' or 'e'. Default 'e'.
227FExponentDelimiter: Char;
228
229// BigDecimal with value -1: unscaled value = -1, scale = 0.
230FMinusOne: BigDecimal;
231
232// BigDecimal with value 0: unscaled value = 0, scale = 0.
233FZero: BigDecimal;
234
235// BigDecimal with value 1: unscaled value = 1, scale = 0.
236FOne: BigDecimal;
237
238// BigDecimal with Value 2: unscaled value = 2, scale = 0.
239FTwo: BigDecimal;
240
241// BigDecimal with value 10: unscaled value = 10, scale = 0.
242FTen: BigDecimal;
243
244// BigDecimal with value 0.5: unscaled value = 5, scale = 1.
245FHalf: BigDecimal;
246
247// BigDecimal with value 0.1: unscaled value = 1, scale = 1.
248FOneTenth: BigDecimal;
249
250{$IFDEF HasClassConstructors}
251class constructor InitClass;
252{$ELSE}
253class procedure InitClass; static;
254{$ENDIF}
255
256// Increments Quotient if its current value, the value of the remainder and the given rounding mode and sign require it.
257class procedure AdjustForRoundingMode(var Quotient: BigInteger; const Divisor, Remainder: BigInteger; Sign: Integer; Mode: RoundingMode); static;
258
259// Divides FValue by a power of ten to remove as many trailing zeros possible without altering its value,
260// i.e. it leaves other digits intact, and adjusts the scale accordingly.
261// Say we have 1.7932400000000 as value, i.e. [FValue=17932400000000, FScale=13], and the target scale
262// is 2, then the result is [179324, 5], which is as close to scale=2 as we can get without altering the value.
263class procedure InPlaceRemoveTrailingZeros(var Value: BigDecimal; TargetScale: Integer); static;
264
265// Converts the current BigDecimal to sign, significand and exponent for the given significand size in bits.
266// Can be used to convert to components for Single, Double and Extended.
267class procedure ConvertToFloatComponents(const Value: BigDecimal; SignificandSize: Integer;
268var Sign: Integer; var Exponent: Integer; var Significand: UInt64); static;
269
270// Converts the current sign, significand and exponent, extracted from a Single, Double or Extended,
271// into a BigDecimal.
272class procedure ConvertFromFloatComponents(Sign: TValueSign; Exponent: Integer; Significand: UInt64;
273var Result: BigDecimal); static;
274
275// Raises exceptions where the type depends on the error code and the message on the arguments.
276class procedure Error(ErrorCode: TErrorCode; ErrorInfo: array of const); static;
277
278// Gets a BigInteger of the given power of five, either from a prefilled array or using BigInteger.Pow.
279class function GetPowerOfFive(N: Integer): BigInteger; static;
280
281// Gets a BigInteger of the given power of ten, either from a prefilled array or using BigInteger.Pow.
282class function GetPowerOfTen(N: Integer): BigInteger; static;
283
284// Initialize or reset scale and precision to 0.
285procedure Init; inline;
286
287// Checks if the NewScale value is a valid scale value. If so, simply returns NewScale. Otherwise, raises
288// an appropriate exception.
289class function RangeCheckedScale(NewScale: Int32): Integer; static;
290
291// Only allows 'e' or 'E' as exponent delimiter for scientific notation output.
292class procedure SetExponentDelimiter(const Value: Char); static;
293
294
295public
296/// <summary>Creates a BigDecimal with given unscaled value and given scale.</summary>
297constructor Create(const UnscaledValue: BigInteger; Scale: Integer); overload;
298
299{$IFDEF HasExtended}
300/// <summary>Creates a BigDecimal with the same value as the given Extended parameter.</summary>
301/// <exception cref="EInvalidArgument">EInvalidArgument is raised if the parameter contains a NaN or infinity.</exception>
302constructor Create(const E: Extended); overload;
303{$ENDIF}
304
305/// <summary>Creates a BigDecimal with the same value as the given Double parameter.</summary>
306/// <exception cref="EInvalidArgument">EInvalidArgument is raised if the parameter contains a NaN or infinity.</exception>
307constructor Create(const D: Double); overload;
308
309/// <summary>Creates a BigDecimal with the same value as the given Single parameter.</summary>
310/// <exception cref="EInvalidArgument">EInvalidArgument is raised if the parameter contains a NaN or infinity.</exception>
311constructor Create(const S: Single); overload;
312
313/// <summary>Creates a BigDecimal with the value that results from parsing the given string parameter.</summary>
314/// <exception cref="EConvertError">EConvertError is raised if the string cannot be parsed to a valid BigDecimal.</exception>
315constructor Create(const S: string); overload;
316
317/// <summary>Creates a BigDecimal with the same value as the given BigInteger parameter.</summary>
318constructor Create(const UnscaledValue: BigInteger); overload;
319
320/// <summary>Creates a BigDecimal with the same value as the given unsigned 64 bit integer parameter.</summary>
321constructor Create(const U64: UInt64); overload;
322
323/// <summary>Creates a BigDecimal with the same value as the given signed 64 bit integer parameter.</summary>
324constructor Create(const I64: Int64); overload;
325
326/// <summary>Creates a BigDecimal with the same value as the given unsigned 32 bit integer parameter.</summary>
327constructor Create(U32: UInt32); overload;
328
329/// <summary>Creates a BigDecimal with the same value as the given signed 32 bit integer parameter.</summary>
330constructor Create(I32: Int32); overload;
331
332
333// -- Mathematical operators --
334
335/// <summary>Adds two BigDecimals. The new scale is Max(Left.Scale, Right.Scale).</summary>
336/// <param name="Left">The augend</param>
337/// <param name="Right">The addend</param>
338/// <returns><code>Result := Left + Right;</code></returns>
339/// <exception cref="EOverflow">EOverflow is raised if the result would become too big.</exception>
340class operator Add(const Left, Right: BigDecimal): BigDecimal;
341
342/// <summary>Subtracts two BigDecimals. The new scale is Max(Left.Scale, Right.Scale).</summary>
343/// <param name="Left">The minuend</param>
344/// <param name="Right">The subtrahend</param>
345/// <returns><code>Result := Left - Right;</code></returns>
346/// <exception cref="EOverflow">EOverflow is raised if the result would become too big.</exception>
347class operator Subtract(const Left, Right: BigDecimal): BigDecimal;
348
349/// <summary>Multiplies two BigDecimals. The new scale is Left.Scale + Right.Scale.</summary>
350/// <exception cref="EOverflow">EOverflow is raised if the result would become too big.</exception>
351/// <exception cref="EUnderflow">EUnderflow is raised if the result would become too small.</exception>
352class operator Multiply(const Left, Right: BigDecimal): BigDecimal;
353
354/// <summary><para>Divides two BigDecimals.</para>
355/// <para>Uses the default precision and rounding mode to obtain the result.</para>
356/// <para>The target scale is <c>Left.Scale - Right.Scale</c>. The result will approach this target scale as
357/// much as possible by removing any excessive trailing zeros.</para></summary>
358/// <param name="Left">The dividend (enumerator)</param>
359/// <param name="Right">The divisor (denominator)</param>
360/// <returns><code>Result := Left / Right;</code></returns>
361/// <exception cref="EZeroDivide">EZeroDivide is raised if the divisor is zero.</exception>
362/// <exception cref="EOverflow">EOverflow is raised if the result would become too big.</exception>
363/// <exception cref="EUnderflow">EUnderflow is raised if the result would become too small.</exception>
364class operator Divide(const Left, Right: BigDecimal): BigDecimal;
365
366/// <summary>Divides two BigDecimals to obtain an integral result.</summary>
367/// <param name="left">The dividend</param>
368/// <param name="Right">The divisor</param>
369/// <returns><code>Result := Left div Right;</code></returns>
370/// <exception cref="EZeroDivide">EZeroDivide is raised if the divisor is zero.</exception>
371/// <exception cref="EOverflow">EOverflow is raised if the result would become too big.</exception>
372/// <exception cref="EUnderflow">EUnderflow is raised if the result would become too small.</exception>
373class operator IntDivide(const Left, Right: BigDecimal): BigDecimal;
374
375/// <summary>Returns the remainder after Left is divided by right to an integral value.</summary>
376/// <param name="Left">The dividend</param>
377/// <param name="Right">The divisor</param>
378/// <returns><code>Result := Left - Right * (Left div Right);</code></returns>
379/// <exception cref="EZeroDivide">EZeroDivide is raised if the divisor is zero.</exception>
380/// <exception cref="EOverflow">EOverflow is raised if the result would become too big.</exception>
381/// <exception cref="EUnderflow">EUnderflow is raised if the result would become too small.</exception>
382class operator Modulus(const Left, Right: BigDecimal): BigDecimal;
383
384/// <summary>Negates the given BigDecimal.</summary>
385/// <returns><code>Result := -Value;</code></returns>
386class operator Negative(const Value: BigDecimal): BigDecimal;
387
388/// <summary>Called when a BigDecimal is preceded by a unary +. Currently a no-op.</summary>
389/// <returns><code>Result := +Value;</code></returns>
390class operator Positive(const Value: BigDecimal): BigDecimal;
391
392/// <summary>Rounds the given BigDecimal to an Int64.</summary>
393/// <exception cref="EConvertError">EConvertError is raised if the result is too large to fit in an Int64.</exception>
394class operator Round(const Value: BigDecimal): Int64;
395
396/// <summary>Truncates (ronds down towards 0) the given BigDecimal to an Int64.</summary>
397/// <exception cref="EConvertError">EConvertError is raised if the result is too large to fit in an Int64.</exception>
398class operator Trunc(const Value: BigDecimal): Int64;
399
400
401// -- Comparison operators --
402
403/// <summary>Returns True if Left is mathematically less than or equal to Right.</summary>
404/// <param name="Left">The first operand</param>
405/// <param name="Right">The second operand</param>
406/// <returns><code>Result := Left <= Right;</code></returns>
407class operator LessThanOrEqual(const Left, Right: BigDecimal): Boolean;
408
409/// <summary>Returns True if Left is mathematically less than Right.</summary>
410/// <param name="Left">The first operand</param>
411/// <param name="Right">The second operand</param>
412/// <returns><code>Result := Left < Right;</code></returns>
413class operator LessThan(const left, Right: BigDecimal): Boolean;
414
415/// <summary>Returns True if Left is mathematically greater than or equal to Right.</summary>
416/// <param name="Left">The first operand</param>
417/// <param name="Right">The second operand</param>
418/// <returns><code>Result := Left >= Right;</code></returns>
419class operator GreaterThanOrEqual(const Left, Right: BigDecimal): Boolean;
420
421/// <summary>Returns True if Left is mathematically greater than Right.</summary>
422/// <param name="Left">The first operand</param>
423/// <param name="Right">The second operand</param>
424/// <returns><code>Result := Left > Right;</code></returns>
425class operator GreaterThan(const Left, Right: BigDecimal): Boolean;
426
427/// <summary>Returns True if Left is mathematically equal to Right.</summary>
428/// <param name="Left">The first operand</param>
429/// <param name="Right">The second operand</param>
430/// <returns><code>Result := Left = Right;</code></returns>
431class operator Equal(const left, Right: BigDecimal): Boolean;
432
433/// <summary>Returns True if Left is mathematically not equal to Right.</summary>
434/// <param name="Left">The first operand</param>
435/// <param name="Right">The second operand</param>
436/// <returns><code>Result := Left <> Right;</code></returns>
437class operator NotEqual(const Left, Right: BigDecimal): Boolean;
438
439
440// -- Implicit conversion operators --
441
442{$IFDEF HasExtended}
443/// <summary>Returns a BigDecimal with the exact value of the given Extended parameter.</summary>
444class operator Implicit(const E: Extended): BigDecimal;
445{$ENDIF}
446
447/// <summary>Returns a BigDecimal with the exact value of the given Double parameter.</summary>
448class operator Implicit(const D: Double): BigDecimal;
449
450/// <summary>Returns a BigDecimal with the exact value of the given Single parameter.</summary>
451class operator Implicit(const S: Single): BigDecimal;
452
453/// <summary>Returns a BigDecimal with the value parsed from the given string parameter.</summary>
454class operator Implicit(const S: string): BigDecimal;
455
456/// <summary>Returns a BigDecimal with the value of the given BigInteger parameter.</summary>
457class operator Implicit(const UnscaledValue: BigInteger): BigDecimal;
458
459/// <summary>Returns a BigDecimal with the value of the given unsigned 64 bit integer parameter.</summary>
460class operator Implicit(const U: UInt64): BigDecimal;
461
462/// <summary>Returns a BigDecimal with the value of the given signed 64 bit integer parameter.</summary>
463class operator Implicit(const I: Int64): BigDecimal;
464
465/// <summary>Returns a BigDecimal with the value of the given unsigned 64 bit integer parameter.</summary>
466class operator Implicit(const U: UInt32): BigDecimal;
467
468/// <summary>Returns a BigDecimal with the value of the given signed 64 bit integer parameter.</summary>
469class operator Implicit(const I: Int32): BigDecimal;
470
471
472// -- Explicit conversion operators --
473
474{$IFDEF HasExtended}
475/// <summary>Returns an Extended with the best approximation of the given BigDecimal value.
476/// The conversion uses the default rounding mode.</summary>
477/// <exception cref="ERoundingNecessary">ERoundingNecessary is raised if a rounding mode
478/// rmUnnecessary was specified but rounding is necessary after all.</exception>
479class operator Explicit(const Value: BigDecimal): Extended;
480{$ENDIF}
481
482/// <summary>Returns a Double with the best approximation of the given BigDecimal value.
483/// The conversion uses the default rounding mode.</summary>
484/// <exception cref="ERoundingNecessary">ERoundingNecessary is raised if a rounding mode
485/// rmUnnecessary was specified but rounding is necessary after all.</exception>
486class operator Explicit(const Value: BigDecimal): Double;
487
488/// <summary>Returns a Single with the best approximation of the given BigDecimal value.
489/// The conversion uses the default rounding mode.</summary>
490/// <exception cref="ERoundingNecessary">ERoundingNecessary is raised if a rounding mode
491/// rmUnnecessary was specified but rounding is necessary after all.</exception>
492class operator Explicit(const Value: BigDecimal): Single;
493
494/// <summary>Returns a string representation of the given BigDecimal value.</summary>
495class operator Explicit(const Value: BigDecimal): string;
496
497/// <summary>Returns a BigInteger with the rounded value of the given BigDecimal value.
498/// The conversion uses the rounding mode rmDown, i.e. it truncates.</summary>
499class operator Explicit(const Value: BigDecimal): BigInteger;
500
501/// <summary>Returns an unsigned 64 bit integer with the rounded value of the given BigDecimal value.
502/// The conversion uses the default rounding mode rmDown, i.e. it truncates.</summary>
503/// <remarks><para>If the value of the rounded down BigDecimal does not fit in an UInt64, only the low
504/// 64 bits of that value are used to form the result.</para>
505/// <para>This is analogue to</para>
506/// <code> myByte := Byte(MyUInt64);</code>
507/// <para>Only the low 8 bits of myUInt64 are copied to the byte.</para></remarks>
508class operator Explicit(const Value: BigDecimal): UInt64;
509
510/// <summary>Returns a signed 64 bit integer with the rounded value of the given BigDecimal value.
511/// The conversion uses the default rounding mode rmDown, i.e. it truncates.</summary>
512/// <remarks><para>If the value of the rounded down BigDecimal does not fit in an Int64, only the low
513/// 64 bits of that value are used to form the result.</para>
514/// <para>This is analogue to</para>
515/// <code> myByte := Byte(MyUInt64);</code>
516/// <para>Only the low 8 bits of myUInt64 are copied to the byte.</para></remarks>
517class operator Explicit(const Value: BigDecimal): Int64;
518
519/// <summary>Returns an unsigned 32 bit integer with the rounded value of the given BigDecimal value.
520/// The conversion uses the default rounding mode rmDown, i.e. it truncates.</summary>
521/// <remarks><para>If the value of the rounded down BigDecimal does not fit in an UInt32, only the low
522/// 32 bits of that value are used to form the result.</para>
523/// <para>This is analogue to</para>
524/// <code> myByte := Byte(MyUInt32);</code>
525/// <para>Only the low 8 bits of myUInt32 are copied to the byte.</para></remarks>
526class operator Explicit(const Value: BigDecimal): UInt32;
527
528/// <summary>Returns a signed 32 bit integer with the rounded value of the given BigDecimal value.
529/// The conversion uses the default rounding mode rmDown, i.e. it truncates.</summary>
530/// <remarks><para>If the value of the rounded down BigDecimal does not fit in an Int32, only the low
531/// 32 bits of that value are used to form the result.</para>
532/// <para>This is analogue to</para>
533/// <code> myByte := Byte(MyUInt32);</code>
534/// <para>Only the low 8 bits of myUInt32 are copied to the byte.</para></remarks>
535class operator Explicit(const Value: BigDecimal): Int32;
536
537
538// -- Conversion functions --
539
540{$IFDEF HasExtended}
541function AsExtended: Extended;
542{$ENDIF}
543function AsDouble: Double;
544function AsSingle: Single;
545function AsBigInteger: BigInteger;
546function AsUInt64: UInt64;
547function AsInt64: Int64;
548function AsUInt32: UInt32;
549function AsInt32: Int32;
550
551
552// -- Mathematical functions --
553
554/// <summary>Returns the sum of the given parameters. The new scale is Max(Left.Scale, Right.Scale).</summary>
555class function Add(const Left, Right: BigDecimal): BigDecimal; overload; static;
556
557/// <summary>Returns the difference of the given parameters. The new scale is Max(Left.Scale, Right.Scale).</summary>
558class function Subtract(const Left, Right: BigDecimal): BigDecimal; overload; static;
559
560/// <summary>Returns the product ofthe given parameters. The new scale is Left.Scale + Right.Scale.</summary>
561class function Multiply(const Left, Right: BigDecimal): BigDecimal; overload; static;
562
563/// <summary><para>Returns the quotient of the given parameters. Left is the dividend, Right the divisor.</para>
564/// <para>Raises an exception if the value of Right is equal to 0.</para>
565/// <para>Uses the default rounding mode and precision.
566/// Raises an exception if the rounding mode is rmUnnecessary, but rounding turns out to be necessary.</para>
567/// <para>The preferred new scale is Left.Scale - Right.Scale. Removes any trailing zero digits to
568/// approach that preferred scale without altering the significant digits.</para></summary>
569class function Divide(const Left, Right: BigDecimal): BigDecimal; overload; static;
570
571/// <summary><para>Returns the quotient of the given parameters. Left is the dividend, Right the divisor.</para>
572/// <para>Raises an exception if the value of Right is equal to 0.</para>
573/// <para>Uses the given rounding mode and precision.
574/// Raises an exception if the rounding mode is rmUnnecessary, but rounding turns out to be necessary.</para>
575/// <para>The preferred new scale is Left.Scale - Right.Scale. Removes any trailing zero digits to
576/// approach that preferred scale without altering the significant digits.</para></summary>
577class function Divide(const Left, Right: BigDecimal; Precision: Integer; ARoundingMode: RoundingMode): BigDecimal; overload; static;
578
579/// <summary><para>Returns the quotient of the given parameters. Left is the dividend, Right the divisor.</para>
580/// <para>Raises an exception if the value of Right is equal to 0.</para>
581/// <para>Uses the given rounding mode and the default precision.
582/// Raises an exception if the rounding mode is rmUnnecessary, but rounding turns out to be necessary.</para>
583/// <para>The preferred new scale is Left.Scale - Right.Scale. Removes any trailing zero digits to
584/// approach that preferred scale without altering the significant digits.</para></summary>
585class function Divide(const Left, Right: BigDecimal; Precision: Integer): BigDecimal; overload; static;
586
587/// <summary><para>Returns the quotient of the given parameters. Left is the dividend, Right the divisor.</para>
588/// <para>Raises an exception if the value of Right is equal to 0.</para>
589/// <para>Uses the default rounding mode and the given precision.
590/// Raises an exception if the rounding mode is rmUnnecessary, but rounding turns out to be necessary.</para>
591/// <para>The preferred new scale is Left.Scale - Right.Scale. Removes any trailing zero digits to
592/// approach that preferred scale without altering the significant digits.</para></summary>
593class function Divide(const Left, Right: BigDecimal; ARoundingMode: RoundingMode): BigDecimal; overload; static;
594
595/// <summary>Returns the negated value of the given BigDecimal parameter.</summary>
596class function Negate(const Value: BigDecimal): BigDecimal; overload; static;
597
598/// <summary>Rounds the value of the given BigDecimal parameter to a signed 64 bit integer. Uses the default
599/// rounding mode for the conversion.</summary>
600class function Round(const Value: BigDecimal): Int64; overload; static;
601
602/// <summary>Rounds the value of the given BigDecimal parameter to a signed 64 bit integer. Uses the default
603/// rounding mode for the conversion.</summary>
604class function Round(const Value: BigDecimal; ARoundingMode: RoundingMode): Int64; overload; static;
605
606/// <summary><para>Returns the BigDecimal remainder after the division of the two parameters.</para>
607/// <para>Uses the default precision and rounding mode for the division.</para></summary>
608/// <returns><para>The result has the value of</para>
609/// <code> Left - (Left / Right).Int * Right</code></returns>
610class function Remainder(const Left, Right: BigDecimal): BigDecimal; static;
611
612/// <summary>Returns the absolute (non-negative) value of the given BigDecimal.</summary>
613class function Abs(const Value: BigDecimal): BigDecimal; overload; static;
614
615/// <summary>Returns the square of the given BigDecimal.<summary>
616class function Sqr(const Value: BigDecimal): BigDecimal; overload; static;
617
618/// <summary>Returns the square root of the given BigDecimal, using the given precision.</summary>
619class function Sqrt(const Value: BigDecimal; Precision: Integer): BigDecimal; overload; static;
620
621/// <summary>Returns the square root of the given BigDecimal, using the default precision.</summary>
622class function Sqrt(const Value: BigDecimal): BigDecimal; overload; static;
623
624/// <summary>Returns the integer power of the given BigDecimal, in unlimited precision.</summary>
625class function IntPower(const Base: BigDecimal; Exponent: Integer): BigDecimal; overload; static;
626
627/// <summary>Returns the integer power of the given BigDecimal, in the given precision.</summary>
628class function IntPower(const Base: BigDecimal; Exponent, Precision: Integer): BigDecimal; overload; static;
629
630
631// -- Comparison functions --
632
633/// <summary>Returns 1 if Left is matehamtically greater than Right, 0 if Left is mathematically equal to Right and
634/// -1 is Left is matheamtically less than Right.</summary>
635class function Compare(const Left, Right: BigDecimal): TValueSign; static;
636
637/// <summary>Returns the maximum of the two given BigDecimal values.</summary>
638class function Max(const Left, Right: BigDecimal): BigDecimal; static;
639
640/// <summary>Returns the minimum of the two given BigDecimal values.</summary>
641class function Min(const Left, Right: BigDecimal): BigDecimal; static;
642
643
644// -- Parsing --
645
646/// <summary>Tries to parse the given string as a BigDecimal into Res, using the given format settings.</summary>
647/// <returns>Returns only True of the function was successful.</returns>
648class function TryParse(const S: string; const Settings: TFormatSettings; out Value: BigDecimal): Boolean;
649overload; static;
650
651/// <summary>Tries to parse the given string as a BigDecimal into Res, using the system invariant format
652/// settings.</summary>
653/// <returns>Returns only True of the function was successful.</returns>
654class function TryParse(const S: string; out Value: BigDecimal): Boolean;
655overload; static;
656
657/// <summary>Returns the BigDecimal with a value as parsed from the given string, using the given
658/// format settings.</summary>
659/// <exception cref="EConvertError">EConvertError is raised if the string cannot be parsed to a valid BigDecimal.</exception>
660class function Parse(const S: string; const Settings: TFormatSettings): BigDecimal; overload; static;
661
662/// <summary>Returns the BigDecimal with a value as parsed from the given string, using the system
663/// invariant format settings.</summary>
664/// <exception cref="EConvertError">EConvertError is raised if the string cannot be parsed to a valid BigDecimal.</exception>
665class function Parse(const S: string): BigDecimal; overload; static;
666
667
668// -- Instance methods --
669
670/// <summary>Returns true if the current BigDecimal's value equals zero.</summary>
671function IsZero: Boolean;
672
673/// <summary>Returns true if the current BigDecimal's value is positive.</summary>
674function IsPositive: Boolean;
675
676/// <summary>Returns true if the current BigDecimal's value is negative.</summary>
677function IsNegative: Boolean;
678
679/// <summary>Returns the sign of the current BigDecimal: -1 if negative, 0 if zero, 1 if positive.</summary>
680function Sign: TValueSign;
681
682/// <summary>Returns the absolute (i.e. non-negative) value of the current BigDecimal.</summary>
683function Abs: BigDecimal; overload;
684
685/// <summary>Rounds the current BigDecimal to a value with at most Digits digits, using the given rounding
686/// mode.</summary>
687/// <exception cref="ERoundingNecessary">ERoundingNecessary is raised if a rounding mode
688/// rmUnnecessary was specified but rounding is necessary after all.</exception>
689/// <remarks><para>The System.Math.RoundTo function uses the floating point equivalent of rmNearestEven, while
690/// System.Math.SimpleRoundTo uses the equivalent of rmNearestUp. This function is more versatile.</para>
691/// <para>This is exactly equivalent to</para>
692/// <code> RoundToScale(-Digits, ARoundingMode);</code></remarks>
693function RoundTo(Digits: Integer; ARoundingMode: RoundingMode): BigDecimal; overload;
694
695/// <summary>Rounds the current BigDecimal to a value with at most Digits digits, using the default rounding
696/// mode.</summary>
697/// <exception cref="ERoundingNecessary">ERoundingNecessary is raised if a rounding mode
698/// rmUnnecessary was specified but rounding is necessary after all.</exception>
699/// <remarks><para>The System.Math.RoundTo function uses the floating point equivalent of rmNearestEven, while
700/// System.Math.SimpleRoundTo uses the equivalent of rmNearestUp. This function is more versatile.</para>
701/// <para>This is exactly equivalent to</para>
702/// <code> RoundToScale(-Digits, DefaultRoundingMode);</code></remarks>
703function RoundTo(Digits: Integer): BigDecimal; overload;
704
705/// <summary>Rounds the current BigDecimal to a value with the given scale, using the given rounding
706/// mode.</summary>
707/// <exception cref="ERoundingNecessary">ERoundingNecessary is raised if a rounding mode
708/// rmUnnecessary was specified but rounding is necessary after all.</exception>
709function RoundToScale(NewScale: Integer; ARoundingMode: RoundingMode): BigDecimal;
710
711/// <summary>Rounds the current Bigdecimal to a certain precision (number of significant digits).</summary>
712/// <exception cref="ERoundingNecessary">ERoundingNecessary is raised if a rounding mode
713/// rmUnnecessary was specified but rounding is necessary after all.</exception>
714function RoundToPrecision(APrecision: Integer): BigDecimal; overload;
715
716/// <summary>Returns a new BigDecimal with the decimal point shifted to the left by the given number of positions</summary>
717function MovePointLeft(Digits: Integer): BigDecimal;
718
719/// <summary>Returns a new BigDecimal with the decimal point shifted to the right by the given number of positions</summary>
720function MovePointRight(Digits: Integer): BigDecimal;
721
722/// <summary>Returns a value with any fraction (digits after the decimal point) removed from the current
723/// BigDecimal.</summary>
724/// <remarks>Example: BigDecimal('1234.5678') results in BigDecimal('1234').</remarks>.
725function Int: BigDecimal;
726
727/// <summary>Returns a signed 64 bit integer with any fraction (digits after the decimal point) removed
728/// from the current BigDecimal.</summary>
729/// <exception cref="EConvertError">EConvertError is raised if the result does not fit in an Int64.</exception>
730function Trunc: Int64;
731
732/// <summary>Returns a BigDecimal containing only the fractional part (digits after the decimal point) of
733/// the current BigDecimal.</summary>
734/// <remarks>Example: BigDecimal('1234.5678') results in BigDecimal('0.5678').</remarks>
735function Frac: BigDecimal;
736
737/// <summary>Returns a BigDecimal rounded down, towards negative infinity, to the next integral value.</summary>
738/// <remarks>Example: BigDecimal('1234.5678') results in BigDecimal('1234');</remarks>
739function Floor: BigDecimal;
740
741/// <summary>Returns a BigDecimal rounded up, towards positive infinity, to the next integral value.</summary>
742/// <remarks>Example: BigDecimal('1234.5678') results in BigDecimal('1235');</remarks>
743function Ceil: BigDecimal;
744
745/// <summary>Returns the number of significant digits of the current BigDecimal.</summary>
746function Precision: Integer;
747
748/// <summary>Returns the reciprocal of the current BigDecimal, using the given precision</summary>
749/// <exception cref="EZeroDivide">EZeroDivide is raised if the current BigDecimal is zero.</exception>
750function Reciprocal(Precision: Integer): BigDecimal; overload;
751
752/// <summary>Returns the reciprocal of the current BigDecimal, using the given precision</summary>
753function Reciprocal: BigDecimal; overload;
754
755/// <summary>Returns a new BigDecimal with all trailing zeroes (up to the preferred scale) removed from the
756/// current BigDecimal. No significant digits will be removed and the numerical value of the result compares
757/// as equal to the original value.</summary>
758/// <param name="TargetScale">The scale up to which trailing zeroes can be removed. It is possible that
759/// fewer zeroes are removed, but never more than necessary to reach the preferred scale.</param>
760/// <remarks><para>Note that no rounding is required. Removal stops at the rightmost non-zero digit.</para>
761/// <para>Example: BigDecimal('1234.5678900000').RemoveTrailingZeros(3) results in
762/// BigDecimal('1234.56789').</para></remarks>
763function RemoveTrailingZeros(TargetScale: Integer): BigDecimal;
764
765/// <summary>Returns the square root of the current BigDecimal, with the given precision.</summary>
766function Sqrt(Precision: Integer): BigDecimal; overload;
767
768/// <summary>Returns the square root of the current BigDecimal, with the default precision.</summary>
769function Sqrt: BigDecimal; overload;
770
771/// <summary>Returns the integer power of the current BigDecimal, with unlimited precision.</summary>
772function IntPower(Exponent: Integer): BigDecimal; overload;
773
774/// <summary>Returns the integer power of the current BigDecimal, with the given precision.</summary>
775function IntPower(Exponent, Precision: Integer): BigDecimal; overload;
776
777/// <summary>Returns the square of the current BigDecimal.</summary>
778function Sqr: BigDecimal; overload;
779
780/// <summary>Returns the unit of least precision of the current BigDecimal.</summary>
781function ULP: BigDecimal;
782
783/// <summary>Returns a plain string of the BigDecimal value. This is sometimes called 'decimal notation', and
784/// shows the value without the use of exponents.</summary>
785function ToPlainString: string; overload;
786
787function ToPlainString(const Settings: TFormatSettings): string; overload;
788
789/// <summary>Returns a plain string under certain conditions, otherwise returns scientific notation.</summary>
790/// <remarks>This does not use FormatSettings. The output is roundtrip, so it is a valid string that can be
791/// parsed using Parse() or TryParse().</remarks>
792function ToString: string; overload;
793
794/// <summary>Returns a plain string under certain conditions, otherwise returns scientific notation.</summary>
795/// <remarks>This uses the given FormatSettings for the decimal point Char.</remarks>
796function ToString(const Settings: TFormatSettings): string; overload;
797
798
799// -- Class properties --
800
801/// <summary>The rounding mode to be used if no specific mode is given.</summary>
802class property DefaultRoundingMode: RoundingMode read FDefaultRoundingMode write FDefaultRoundingMode;
803
804/// <summary>The (maximum) precision to be used for e.g. division if the operation would otherwise result in a
805/// non-terminating decimal expansion, i.e. if there is no exact representable decimal result, e.g. when
806/// dividing <code>BigDecimal(1) / BigDecimal(3) (= 0.3333333...)</code></summary>
807class property DefaultPrecision: Integer read FDefaultPrecision write FDefaultPrecision;
808
809/// <summary>If set to False, division will not try to reduce the trailing zeros to match the
810/// preferred scale. That is faster, but usually produces bigger decimals</summary>
811class property ReduceTrailingZeros: Boolean read FReduceTrailingZeros write FReduceTrailingZeros;
812
813/// <summary>The string to be used to delimit the exponent part in scientific notation output.</summary>
814/// <remarks>Currently, only 'e' and 'E' are allowed. Setting any other value will be ignored. The default is 'e',
815/// because a lower case letter 'e' is usually more easily distinguished between digits '0'..'9'.</remarks>
816class property ExponentDelimiter: Char read FExponentDelimiter write SetExponentDelimiter;
817
818/// <summary>BigDecimal with value -1: unscaled value = -1, scale = 0.</summary>
819class property MinusOne: BigDecimal read FMinusOne;
820
821/// <summary>BigDecimal with value 0: unscaled value = 0, scale = 0.</summary>
822class property Zero: BigDecimal read FZero;
823
824/// <summary>BigDecimal with value 1: unscaled value = 1, scale = 0.</summary>
825class property One: BigDecimal read FOne;
826
827/// <summary>BigDecimal with value 2: unscaled value = 2, scale = 0.</summary>
828class property Two: BigDecimal read FTwo;
829
830/// <summary>BigDecimal with value 10: unscaled value = 10, scale = 0.</summary>
831class property Ten: BigDecimal read FTen;
832
833/// <summary>BigDecimal with value 0.5: unscaled value = 5, scale = 1.</summary>
834class property Half: BigDecimal read FHalf;
835
836/// <summary>BigDecimal with value 0.1: unscaled value = 1, scale = 1.</summary>
837class property OneTenth: BigDecimal read FOneTenth;
838
839
840// -- Instance properties --
841
842/// <summary>The scale of the current BigDecimal. This is the power of ten by which the UnscaledValue must
843/// be divided to get the value of the BigDecimal. Negative scale values denote multiplying by a
844/// power of ten.</summary>
845/// <remarks>So 1.79e+308 can be stored as UnscaledValue = 179 and Scale = -306, requiring only a small BigInteger
846/// with a precision of 3, and not a large one of 308 digits.</remarks>
847property Scale: Integer read FScale;
848
849/// <summary>The unscaled value of the current BigDecimal. This is the BigInteger than contains the
850/// significant digits of the BigDecimal. It is then scaled (in powers of ten) by Scale.</summary>
851property UnscaledValue: BigInteger read FValue;
852end;
853
854{$HPPEMIT END '#include "Velthuis.BigDecimals.operators.hpp"'}
855
856implementation
857
858{$RANGECHECKS OFF}
859{$OVERFLOWCHECKS OFF}
860
861uses
862Velthuis.FloatUtils, Velthuis.Numerics, Velthuis.StrConsts;
863
864var
865PowersOfTen: TArray<BigInteger>;
866
867function InvariantSettings: TFormatSettings;
868{$IF RTLVersion >= 29.0}
869begin
870// XE8 and higher
871Result := TFormatSettings.Invariant;
872end;
873{$ELSE}
874const
875Settings: TFormatSettings =
876(
877CurrencyString: #$00A4;
878CurrencyFormat: 0;
879CurrencyDecimals: 2;
880DateSeparator: '/';
881TimeSeparator: ':';
882ListSeparator: ',';
883ShortDateFormat: 'MM/dd/yyyy';
884LongDateFormat: 'dddd, dd MMMMM yyyy HH:mm:ss';
885TimeAMString: 'AM';
886TimePMString: 'PM';
887ShortTimeFormat: 'HH:mm';
888LongTimeFormat: 'HH:mm:ss';
889ShortMonthNames: ('Jan', 'Feb', 'Mar', 'Apr', 'May,', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
890LongMonthNames: ('January', 'February', 'March', 'April', 'May', 'June',
891'July', 'August', 'September', 'October', 'November', 'December');
892ShortDayNames: ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
893LongDayNames: ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
894ThousandSeparator: ',';
895DecimalSeparator: '.';
896TwoDigitYearCenturyWindow: 50;
897NegCurrFormat: 0;
898);
899begin
900Result := Settings;
901end;
902{$IFEND}
903
904{ BigDecimal }
905
906function BigDecimal.Abs: BigDecimal;
907begin
908if Self.FValue.IsNegative then
909Result := -Self
910else
911Result := Self;
912end;
913
914class function BigDecimal.Abs(const Value: BigDecimal): BigDecimal;
915begin
916Result := Value.Abs;
917end;
918
919//////////////////////////////////////////////////////////////////////////////////////////////////////////
920// //
921// Adding and subtracting is easy: the operand with the lowest scale is scaled up to the scale of the //
922// other operand. Then the unscaled values (FValue members) can be added or subtracted respectively. //
923// //
924//////////////////////////////////////////////////////////////////////////////////////////////////////////
925
926class function BigDecimal.Add(const Left, Right: BigDecimal): BigDecimal;
927var
928L, R: BigInteger;
929begin
930Result.Init;
931if Left.IsZero then
932if Right.IsZero then
933Exit(BigDecimal.Zero)
934else
935Exit(Right)
936else if Right.IsZero then
937Exit(Left);
938if Left.Scale > Right.Scale then
939begin
940L := Left.FValue;
941R := Right.FValue * GetPowerOfTen(Left.Scale - Right.Scale);
942Result.FScale := Left.FScale;
943end
944else
945begin
946L := Left.FValue * GetPowerOfTen(Right.Scale - Left.Scale);
947R := Right.FValue;
948Result.FScale := Right.FScale;
949end;
950Result.FValue := L + R;
951end;
952
953class operator BigDecimal.Add(const Left, Right: BigDecimal): BigDecimal;
954begin
955Result := Add(Left, Right);
956end;
957
958////////////////////////////////////////////////////
959// //
960// See comment on rounding near RoundToScale(). //
961// //
962////////////////////////////////////////////////////
963
964class procedure BigDecimal.AdjustForRoundingMode(var Quotient: BigInteger; const Divisor, Remainder: BigInteger; Sign: Integer; Mode: RoundingMode);
965begin
966if not Remainder.IsZero then
967case Mode of
968rmUp: // 1.7x --> 1.8, -1.7x --> -1.8
969Inc(Quotient);
970rmDown: // 1.7x --> 1.7, -1.7x --> -1.7
971; // No action required; truncation is default.
972rmCeiling: // 1.7x --> 1.8, -1.7x --> -1.7
973if Sign >= 0 then
974Inc(Quotient);
975rmFloor: // 1.7x --> 1.7, -1.7x --> -1.8
976if Sign <= 0 then
977Inc(Quotient);
978rmNearestUp, rmNearestDown, rmNearestEven:
979if Remainder + Remainder > Divisor then // 1.78 --> 1.8, 1.72 --> 1.7, 1.75 --> see next
980Inc(Quotient)
981else if Remainder + Remainder = Divisor then // the "Half" condition.
982if (Mode = rmNearestUp) or ((Mode = rmNearestEven) and not Quotient.IsEven) then
983Inc(Quotient);
984rmUnnecessary: // No remainder allowed.
985Error(ecRounding, []);
986end;
987end;
988
989{$IFDEF HasExtended}
990function BigDecimal.AsExtended: Extended;
991begin
992Result := Extended(Self);
993if IsInfinite(Result) then
994Error(ecConversion, ['BigDecimal', 'Extended']);
995end;
996{$ENDIF}
997
998function BigDecimal.AsDouble: Double;
999begin
1000Result := Double(Self);
1001if IsInfinite(Result) then
1002Error(ecConversion, ['BigDecimal', 'Double']);
1003end;
1004
1005function BigDecimal.AsSingle: Single;
1006begin
1007Result := Single(Self);
1008if IsInfinite(Result) then
1009Error(ecConversion, ['BigDecimal', 'Single']);
1010end;
1011
1012function BigDecimal.AsBigInteger: BigInteger;
1013begin
1014Result := BigInteger(Self);
1015if Self.Scale > 0 then
1016Error(ecRounding, []);
1017end;
1018
1019function BigDecimal.AsUInt64: UInt64;
1020var
1021D: BigDecimal;
1022begin
1023D := Self.RoundToScale(0, rmUnnecessary); // Throws if rounding necessary
1024try
1025Result := D.UnscaledValue.AsUInt64; // Throws if too big
1026except
1027Error(ecConversion, ['BigDecimal', 'UInt64']);
1028Result := 0;
1029end;
1030end;
1031
1032function BigDecimal.AsInt64: Int64;
1033var
1034D: BigDecimal;
1035begin
1036D := Self.RoundToScale(0, rmUnnecessary);
1037try
1038Result := D.UnscaledValue.AsInt64;
1039except
1040Error(ecConversion, ['BigDecimal', 'Int64']);
1041Result := 0;
1042end;
1043end;
1044
1045function BigDecimal.AsUInt32: UInt32;
1046var
1047D: BigDecimal;
1048begin
1049D := Self.RoundToScale(0, rmUnnecessary);
1050try
1051Result := D.UnscaledValue.AsCardinal;
1052except
1053Error(ecConversion, ['BigDecimal', 'UInt32']);
1054Result := 0;
1055end;
1056end;
1057
1058function BigDecimal.AsInt32: Int32;
1059var
1060D: BigDecimal;
1061begin
1062D := Self.RoundToScale(0, rmUnnecessary);
1063try
1064Result := D.UnscaledValue.AsInteger;
1065except
1066Error(ecConversion, ['BigDecimal', 'Int32']);
1067Result := 0;
1068end;
1069end;
1070
1071// Does a "binary search" to remove trailing zeros. This is much faster (10 times or more) than repeatedly
1072// dividing by BigInteger.Ten until there is a remainder, even for relatively small numbers of trailing zeros.
1073// Since this modifies Value, it cannot be made public, but there is a public version of this, called
1074// RemoveTrailingZeros.
1075class procedure BigDecimal.InPlaceRemoveTrailingZeros(var Value: BigDecimal; TargetScale: Integer);
1076var
1077L, H, M, LSign: Integer;
1078LValue, LDivisor, LQuotient, LRemainder: BigInteger;
1079LScale: Integer;
1080begin
1081LSign := Value.FValue.Sign;
1082LValue := BigInteger.Abs(Value.FValue);
1083LScale := Value.FScale;
1084LQuotient := Value.FValue;
1085L := TargetScale;
1086H := LScale;
1087while H > L do
1088begin
1089// Take the middle value and do a DivMod
1090M := (L + H) div 2;
1091if M = LScale then
1092Break;
1093LDivisor := GetPowerOfTen(LScale - M);
1094if not LValue.IsEven then
1095// Odd numbers can't be divisible by ten, so cut short.
1096L := M + 1
1097else
1098begin
1099BigInteger.DivMod(LValue, LDivisor, LQuotient, LRemainder);
1100if LRemainder.IsZero then
1101begin
1102// Remainder was 0, so use the quotient (which has trailing zeroes removed) as new base and try to remove
1103// more zeroes on the left.
1104H := M;
1105LValue := LQuotient;
1106// Update the scale accordingly.
1107LScale := M;
1108end
1109else
1110// Remainder was not 0, so search further to the right.
1111L := M + 1;
1112end;
1113end;
1114
1115// Value and scale may still be off by one.
1116if (LScale > TargetScale) and (LValue >= BigInteger.Ten) then
1117begin
1118BigInteger.DivMod(LValue, BigInteger.Ten, LQuotient, LRemainder);
1119if LRemainder.IsZero then
1120begin
1121LValue := LQuotient;
1122Dec(LScale);
1123end
1124end;
1125
1126LValue.Sign := LSign;
1127Value.Create(LValue, LScale);
1128end;
1129
1130class function BigDecimal.Compare(const Left, Right: BigDecimal): TValueSign;
1131const
1132Values: array[Boolean] of Integer = (-1, 1);
1133var
1134L, R: BigInteger;
1135begin
1136if Left.FValue.IsZero then
1137if Right.FValue.IsZero then
1138Exit(0)
1139else
1140Exit(Values[Right.FValue.IsNegative])
1141else if Right.FValue.IsZero then
1142Exit(Values[Left.FValue.IsPositive]);
1143if Left.FScale > Right.FScale then
1144begin
1145L := Left.FValue;
1146R := Right.FValue * GetPowerOfTen(RangeCheckedScale(Left.FScale - Right.FScale));
1147end
1148else
1149begin
1150L := Left.FValue * GetPowerOfTen(RangeCheckedScale(Right.FScale - Left.FScale));
1151R := Right.FValue;
1152end;
1153Result := BigInteger.Compare(L, R);
1154end;
1155
1156// Converts Value to components for binary FP format, with Significand, binary Exponent and Sign. Significand
1157// (a.k.a. mantissa) is SignificandSize bits. This can be used for conversion to Extended, Double and Single, and,
1158// if desired, IEEE 754-2008 binary128 (note: binary32 is equivalent to Single and binary64 to Double).
1159class procedure BigDecimal.ConvertToFloatComponents(const Value: BigDecimal; SignificandSize: Integer;
1160var Sign, Exponent: Integer; var Significand: UInt64);
1161var
1162LDivisor, LQuotient, LRemainder, LLowBit, LSignificand: BigInteger;
1163LBitLen, LScale: Integer;
1164begin
1165if Value.FValue.IsNegative then
1166Sign := -1
1167else
1168Sign := 1;
1169
1170LScale := Value.Scale;
1171Exponent := 0;
1172
1173if LScale < 0 then
1174begin
1175// Get rid of scale while retaining the value:
1176// Reduce scale to 0 and at the same time multiply UnscaledValue with 10^-Scale
1177// Multiplying by 10^-Scale is equivalent to multiplying with 5^-Scale while decrementing exponent by scale.
1178Exponent := -LScale;
1179LSignificand := BigInteger.Abs(Value.FValue) * GetPowerOfFive(Exponent);
1180end
1181else if LScale > 0 then
1182begin
1183// Get rid of scale, but the other way around: shift left as much as necessary (i.e. multiply by 2^-Scale)
1184// and then divide by 5^Scale.
1185Exponent := -LScale;
1186LDivisor := GetPowerOfFive(LScale);
1187LBitLen := LDivisor.BitLength;
1188LSignificand := BigInteger.Abs(Value.FValue) shl (LBitLen + SignificandSize - 1);
1189Dec(Exponent, LBitLen + SignificandSize - 1);
1190BigInteger.DivMod(LSignificand, LDivisor, LQuotient, LRemainder);
1191BigDecimal.AdjustForRoundingMode(LQuotient, LDivisor, LRemainder, Sign, rmNearestEven);
1192LSignificand := LQuotient;
1193end
1194else
1195LSignificand := BigInteger.Abs(Value.FValue);
1196
1197LBitLen := LSignificand.BitLength;
1198if LBitLen > SignificandSize then
1199begin
1200LLowBit := BigInteger.One shl (LBitLen - SignificandSize);
1201LRemainder := (LSignificand and (LLowBit - BigInteger.One)) shl 1;
1202LSignificand := LSignificand shr (LBitLen - SignificandSize);
1203Inc(Exponent, LBitLen - 1);
1204if (LRemainder > LLowBit) or ((LRemainder = LLowBit) and not LSignificand.IsEven) then
1205begin
1206Inc(LSignificand);
1207if LSignificand.BitLength > SignificandSize then
1208begin
1209LSignificand := LSignificand shr 1;
1210Inc(Exponent);
1211end;
1212end
1213end
1214else
1215begin
1216LSignificand := LSignificand shl (SignificandSize - LBitLen);
1217Inc(Exponent, LBitLen - 1);
1218end;
1219
1220Significand := LSignificand.AsUInt64;
1221end;
1222
1223constructor BigDecimal.Create(const UnscaledValue: BigInteger; Scale: Integer);
1224begin
1225Init;
1226FValue := UnscaledValue;
1227FScale := Scale;
1228end;
1229
1230class procedure BigDecimal.ConvertFromFloatComponents(Sign: TValueSign; Exponent: Integer; Significand: UInt64; var Result: BigDecimal);
1231type
1232TUInt64 = packed record
1233Lo, Hi: UInt32;
1234end;
1235var
1236NewUnscaledValue: BigInteger;
1237NewScale: Integer;
1238Shift: Integer;
1239begin
1240Shift := NumberOfTrailingZeros(Significand);
1241
1242Significand := Significand shr Shift;
1243Inc(Exponent, Shift);
1244
1245NewUnscaledValue := Significand;
1246
1247NewScale := 0;
1248if Exponent < 0 then
1249begin
1250// To get rid of the binary exponent (make it 0), BigInt must repeatedly be divided by 2.
1251// This isn't done directly: on each "iteration", BigInt is multipiled by 5 and then the
1252// decimal point is moved by one, which is equivalent with a division by 10.
1253// So, effectively, the result is divided by 2.
1254// Instead of in a loop, this is done directly using Pow()
1255NewUnscaledValue := NewUnscaledValue * BigInteger.Pow(5, -Exponent);
1256NewScale := -Exponent;
1257end
1258else if Exponent > 0 then
1259NewUnscaledValue := NewUnscaledValue shl Exponent;
1260
1261Result := BigDecimal.Create(NewUnscaledValue, NewScale);
1262if Sign < 0 then
1263Result := -Result;
1264end;
1265
1266constructor BigDecimal.Create(const S: Single);
1267var
1268Significand: UInt64;
1269Exponent: Integer;
1270Sign: TValueSign;
1271begin
1272if IsInfinite(S) or IsNan(S) then
1273Error(ecInvalidArg, ['Single']);
1274
1275if S = 0.0 then
1276begin
1277Self := BigDecimal.Zero;
1278Exit;
1279end;
1280
1281Significand := GetSignificand(S);
1282Exponent := GetExponent(S) - 23;
1283Sign := System.Math.Sign(S);
1284
1285ConvertFromFloatComponents(Sign, Exponent, Significand, Self);
1286end;
1287
1288constructor BigDecimal.Create(const D: Double);
1289var
1290Significand: UInt64;
1291Exponent: Integer;
1292Sign: TValueSign;
1293begin
1294if IsInfinite(D) or IsNan(D) then
1295Error(ecInvalidArg, ['Double']);
1296
1297if D = 0.0 then
1298begin
1299Self := BigDecimal.Zero;
1300Exit;
1301end;
1302
1303Significand := GetSignificand(D);
1304Exponent := GetExponent(D) - 52;
1305Sign := System.Math.Sign(D);
1306
1307ConvertFromFloatComponents(Sign, Exponent, Significand, Self);
1308end;
1309
1310{$IFDEF HasExtended}
1311constructor BigDecimal.Create(const E: Extended);
1312var
1313Significand: UInt64;
1314Exponent: Integer;
1315Sign: TValueSign;
1316begin
1317if IsInfinite(E) or IsNan(E) then
1318Error(ecInvalidArg, ['Extended']);
1319
1320if E = 0.0 then
1321begin
1322Self := BigDecimal.Zero;
1323Exit;
1324end;
1325
1326Significand := GetSignificand(E);
1327Exponent := GetExponent(E) - 63;
1328Sign := System.Math.Sign(E);
1329
1330ConvertFromFloatComponents(Sign,Exponent, Significand, Self);
1331end;
1332{$ENDIF}
1333
1334constructor BigDecimal.Create(const S: string);
1335begin
1336Init;
1337if not TryParse(S, InvariantSettings, Self) then
1338Error(ecParse, [S, 'BigDecimal']);
1339end;
1340
1341constructor BigDecimal.Create(const I64: Int64);
1342begin
1343Init;
1344FValue := BigInteger.Create(I64);
1345end;
1346
1347constructor BigDecimal.Create(const U64: UInt64);
1348begin
1349Init;
1350FValue := BigInteger.Create(U64);
1351end;
1352
1353constructor BigDecimal.Create(U32: UInt32);
1354begin
1355Init;
1356FValue := BigInteger.Create(U32);
1357end;
1358
1359constructor BigDecimal.Create(I32: Int32);
1360begin
1361Init;
1362FValue := BigInteger.Create(I32);
1363end;
1364
1365constructor BigDecimal.Create(const UnscaledValue: BigInteger);
1366begin
1367Init;
1368FValue := UnscaledValue;
1369end;
1370
1371class function BigDecimal.Divide(const Left, Right: BigDecimal; Precision: Integer;
1372ARoundingMode: RoundingMode): BigDecimal;
1373var
1374LQuotient, LRemainder, LDivisor: BigInteger;
1375LScale, TargetScale: Integer;
1376LSign: Integer;
1377LMultiplier: Integer;
1378begin
1379
1380///////////////////////////////////////////////////////////////////////////////////////////////////////////
1381// Naively dividing the BigInteger values would result in 0 when e.g. '0.01' and '0.0025' are divided //
1382// (1 div 25 = 0). //
1383// So the dividend must be scaled up by at least Precision powers of ten. The end result must be rounded //
1384// toward the target scale, which is Left.Scale - Right.Scale. //
1385///////////////////////////////////////////////////////////////////////////////////////////////////////////
1386// Is there a way to find out beforehand if we need the full precision? Take the above: 0.01/0.0025 = 4. //
1387// So we would only need to scale up BigInteger(1) to BigInteger(100) and then divide. Is there a way to //
1388// determine this? OTOH, for 0.01/0.003 we need the full precision. Is there a way to determine if a //
1389// division will result in a non-terminating decimal expansion or if it is terminating, where it will //
1390// terminate? //
1391// The decimal expansion will be terminating if the divisor can be reduced to 2^n * 5^m, with n,m >= 0, //
1392// in other words, if it can be reduced to only powers of 2 and 5. //
1393// Not sure if there is a fast way to determine this. I guess not. //
1394///////////////////////////////////////////////////////////////////////////////////////////////////////////
1395
1396if Right.FValue.IsZero then
1397Error(ecDivByZero, []);
1398TargetScale := Left.Scale - Right.Scale;
1399if Left.FValue.IsZero then
1400begin
1401Result.FValue := BigInteger.Zero;
1402Result.FScale := TargetScale;
1403Exit;
1404end;
1405
1406// Determine target sign.
1407LSign := Left.FValue.Sign xor Right.FValue.Sign;
1408
1409// Use positive values (zero was discarded above).
1410// LDivisor := BigInteger.Abs(Right.FValue);
1411LDivisor := Right.FValue;
1412
1413// Determine minimum power of ten with which to multiply the dividend.
1414// Previous code used:
1415// LMultiplier := RangeCheckedScale(Precision + Right.Precision - Left.Precision + 3);
1416// but the code below is 20% faster - Calculating precision can be slow.
1417LMultiplier := RangeCheckedScale(Precision + (Right.FValue.Size - Left.FValue.Size + 1) * 9 + 3);
1418
1419// Do the division of the scaled up dividend by the divisor. Quotient and remainder are needed.
1420BigInteger.DivMod(Left.FValue * GetPowerOfTen(LMultiplier), LDivisor, LQuotient, LRemainder);
1421
1422// Calculate the scale that matches the division.
1423LScale := RangeCheckedScale(TargetScale + LMultiplier);
1424
1425// Create a preliminary result.
1426Result.Create(LQuotient, LScale);
1427
1428// Reduce the precision, if necessary.
1429// Wow! This is slow. Time reduction of >50% if it could be omitted, e.g. if division were
1430// accurate enough already.
1431Result := Result.RoundToScale(RangeCheckedScale(LScale + Precision - Result.Precision), ARoundingMode);
1432// Can this be combined with InPlaceRemoveTrailingZeros?
1433
1434// remove as many trailing zeroes as possible to get as close as possible to the target scale without
1435// changing the value.
1436// This should be optional, as it is slower.
1437if FReduceTrailingZeros then
1438InPlaceRemoveTrailingZeros(Result, TargetScale);
1439
1440// Finally, set the sign of the result.
1441Result.FValue.Sign := LSign;
1442end;
1443
1444class function BigDecimal.Divide(const Left, Right: BigDecimal): BigDecimal;
1445begin
1446Result := Divide(Left, Right, DefaultPrecision, DefaultRoundingMode);
1447end;
1448
1449class function BigDecimal.Divide(const Left, Right: BigDecimal; Precision: Integer): BigDecimal;
1450begin
1451Result := Divide(Left, Right, Precision, DefaultRoundingMode);
1452end;
1453
1454class function BigDecimal.Divide(const Left, Right: BigDecimal; ARoundingMode: RoundingMode): BigDecimal;
1455begin
1456Result := Divide(Left, Right, DefaultPrecision, ARoundingMode);
1457end;
1458
1459class operator BigDecimal.Divide(const Left, Right: BigDecimal): BigDecimal;
1460begin
1461Result := Divide(Left, Right, DefaultPrecision, DefaultRoundingMode);
1462end;
1463
1464class operator BigDecimal.Equal(const Left, Right: BigDecimal): Boolean;
1465begin
1466Result := Compare(Left, Right) = 0;
1467end;
1468
1469class procedure BigDecimal.Error(ErrorCode: TErrorCode; ErrorInfo: array of const);
1470begin
1471// Raise an exception that matches the given error code. The message is determined by the
1472// format strings and the Args parameter.
1473// Note that, as much as possible, existing exceptions from the Delphi runtime library are used.
1474case ErrorCode of
1475ecParse:
1476// Not a valid BigDecimal string representation.
1477raise EConvertError.CreateFmt(SErrorParsingFmt, ErrorInfo);
1478ecDivByZero:
1479// Division by zero.
1480raise EZeroDivide.Create(SDivisionByZero);
1481ecConversion:
1482// BigDecimal too large for conversion to...
1483raise EConvertError.CreateFmt(SConversionFailedFmt, ErrorInfo);
1484ecOverflow:
1485// Scale would become too low.
1486raise EOverflow.Create(SOverflow);
1487ecUnderflow:
1488// Scale would become too high.
1489raise EUnderflow.Create(SUnderflow);
1490ecInvalidArg:
1491// Parameter is NaN or +/-Infinity.
1492raise EInvalidArgument.CreateFmt(SInvalidArgumentFloatFmt, ErrorInfo);
1493ecRounding:
1494// Rounding was necessary but rmUnnecessary was specified.
1495raise ERoundingNecessary.Create(SRounding);
1496ecExponent:
1497// Exponent outside the allowed range
1498raise EIntPowerExponent.Create(SExponent);
1499else
1500// Invalid operand to operator.
1501raise EInvalidOpException.Create(SInvalidOperation);
1502end;
1503end;
1504
1505class operator BigDecimal.Explicit(const Value: BigDecimal): Single;
1506var
1507LSign, LExponent: Integer;
1508LSignificand: UInt64;
1509LDiff: Integer;
1510LLowBits: UInt32;
1511LRem: UInt32;
1512begin
1513if Value.FValue.IsZero then
1514Exit(0.0);
1515
1516// Convert the given BigDecimal (i.e. UnscaledValue and decimal Scale) to a signficand, sign and binary exponent using
1517// the given size of the significand (24 bits for Single).
1518ConvertToFloatComponents(Value, 24, LSign, LExponent, LSignificand);
1519
1520// Compose calculated sign, significand and exponent into a proper Single.
1521
1522// Handle special values:
1523
1524// * Values too large (infinities).
1525if LExponent > 127 then
1526if LSign < 0 then
1527Result := NegInfinity
1528else
1529Result := Infinity
1530// * Denormals or below (0).
1531else if LExponent < -126 then
1532begin
1533LDiff := -126 - LExponent;
1534if LDiff >= 24 then
1535Exit(0.0);
1536LLowBits := UInt32(1) shl LDiff;
1537LRem := LSignificand and (LLowBits - 1);
1538LSignificand := LSignificand shr LDiff;
1539if LRem + LRem >= LLowBits then
1540Inc(LSignificand);
1541if LSign < 0 then
1542LSignificand := LSignificand or $80000000;
1543Result := PSingle(@LSignificand)^;
1544end
1545else
1546Result := Velthuis.FloatUtils.MakeSingle(LSign, LSignificand, LExponent);
1547end;
1548
1549class operator BigDecimal.Explicit(const Value: BigDecimal): Double;
1550var
1551LSign: Integer;
1552LExponent: Integer;
1553LSignificand: UInt64;
1554LDiff: Integer;
1555LLowBits: UInt64;
1556LRem: UInt64;
1557begin
1558if Value.FValue.IsZero then
1559Exit(0.0);
1560
1561// Convert the given BigDecimal (i.e. UnscaledValue and decimal Scale) to a significand, sign and binary exponent using
1562// the given size of the significand (53 bits for Double).
1563ConvertToFloatComponents(Value, 53, LSign, LExponent, LSignificand);
1564
1565// Compose calculated sign, significand and exponent into a proper Double.
1566
1567// Handle special values:
1568
1569// * Values too large (infinities).
1570if LExponent > 1023 then
1571if LSign < 0 then
1572Result := NegInfinity
1573else
1574Result := Infinity
1575// * Denormals or below (0).
1576else if LExponent < -1022 then
1577begin
1578LDiff := -1022 - LExponent;
1579if LDiff >= 53 then
1580Exit(0.0);
1581
1582LLowBits := UInt64(1) shl LDiff; // mask for the low bits after shift
1583LRem := LSignificand and (LLowBits - 1); // low bits, IOW LSignificand mod 2^LDiff
1584LSignificand := LSignificand shr LDiff; // LSignificand div 2^LDiff
1585if (LRem + LRem > LLowBits) or ((LRem + LRem = LLowBits) and (Odd(LSignificand))) then
1586Inc(LSignificand); // round up
1587if LSign < 0 then
1588LSignificand := LSignificand or $8000000000000000;
1589Result := PDouble(@LSignificand)^;
1590end
1591else
1592Result := Velthuis.FloatUtils.MakeDouble(LSign, LSignificand, LExponent);
1593end;
1594
1595{$IFDEF HasExtended}
1596class operator BigDecimal.Explicit(const Value: BigDecimal): Extended;
1597var
1598LSign, LExponent: Integer;
1599LSignificand: UInt64;
1600LDiff: Integer;
1601LLowBits: UInt64;
1602LExtendedRec: packed record
1603Man: UInt64;
1604Exp: Int16;
1605end;
1606LRem: UInt64;
1607begin
1608ConvertToFloatComponents(Value, 64, LSign, LExponent, LSignificand);
1609
1610// Handle special values
1611// * Infinities
1612if LExponent > 16383 then
1613if LSign < 0 then
1614Result := NegInfinity
1615else
1616Result := Infinity
1617else
1618// * Denormals
1619if LExponent < -16382 then
1620begin
1621LDiff := -16382 - LExponent;
1622if LDiff >= 64 then
1623Exit(0.0);
1624LLowBits := UInt64(1) shl LDiff;
1625LRem := LSignificand and (LLowBits - 1);
1626LSignificand := LSignificand shr LDiff;
1627if LRem + LRem >= LLowBits then
1628Inc(LSignificand);
1629LExtendedRec.Man := LSignificand;
1630LExtendedRec.Exp := 0;
1631if LSign < 0 then
1632LExtendedRec.Exp := LExtendedRec.Exp or Int16($8000);
1633Result := PExtended(@LExtendedRec)^;
1634end
1635else
1636Result := Velthuis.FloatUtils.MakeExtended(LSign, LSignificand, LExponent);
1637end;
1638{$ENDIF}
1639
1640class operator BigDecimal.Explicit(const Value: BigDecimal): string;
1641begin
1642// General format: uses scientific notation when necessary.
1643Result := Value.ToString;
1644end;
1645
1646class operator BigDecimal.Explicit(const Value: BigDecimal): UInt64;
1647var
1648Rounded: BigDecimal;
1649begin
1650Rounded := Value.RoundToScale(0, rmDown);
1651Result := UInt64(Rounded.FValue and High(UInt64));
1652end;
1653
1654class operator BigDecimal.Explicit(const Value: BigDecimal): Int64;
1655var
1656Rounded: BigDecimal;
1657begin
1658Rounded := Value.RoundToScale(0, rmDown);
1659Result := Int64(Rounded.FValue);
1660end;
1661
1662class operator BigDecimal.Explicit(const Value: BigDecimal): UInt32;
1663var
1664Rounded: BigDecimal;
1665begin
1666Rounded := Value.RoundToScale(0, rmDown);
1667Result := UInt32(Rounded.FValue and High(UInt64));
1668end;
1669
1670class operator BigDecimal.Explicit(const Value: BigDecimal): Int32;
1671var
1672Rounded: BigDecimal;
1673begin
1674Rounded := Value.RoundToScale(0, rmDown);
1675Result := Int32(Rounded.FValue);
1676end;
1677
1678function BigDecimal.Frac: BigDecimal;
1679begin
1680Result := BigDecimal.Abs(Self - Self.Int());
1681end;
1682
1683function BigDecimal.Floor: BigDecimal;
1684begin
1685if Scale > 0 then
1686Result := Self.RoundToScale(0, rmFloor)
1687else
1688Result := Self;
1689end;
1690
1691function BigDecimal.Ceil: BigDecimal;
1692begin
1693if Scale > 0 then
1694Result := Self.RoundToScale(0, rmCeiling)
1695else
1696Result := Self;
1697end;
1698
1699class operator BigDecimal.Explicit(const Value: BigDecimal): BigInteger;
1700var
1701Rounded: BigDecimal;
1702begin
1703Rounded := Value.RoundToScale(0, rmDown);
1704Result := Rounded.FValue;
1705end;
1706
1707// Note: 5^N = 10^N div 2^N = 10^N shr N;
1708// Powers of five are required when converting a decimal scale/unscaled value combination to a binary
1709// exponent/significand combination with the same value.
1710class function BigDecimal.GetPowerOfFive(N: Integer): BigInteger;
1711begin
1712Result := GetPowerOfTen(N) shr N;
1713end;
1714
1715// Since a scale denotes powers of ten, powers of ten are required as either multiplicator or divisor.
1716class function BigDecimal.GetPowerOfTen(N: Integer): BigInteger;
1717begin
1718if N >= 0 then
1719begin
1720// If index outside array, enlarge the array.
1721if N > High(PowersOfTen) then
1722SetLength(PowersOfTen, N + 1);
1723Result := PowersOfTen[N];
1724
1725// If the value read is 0, it is obviously invalid, so calculate power and store it at this index.
1726if Result.IsZero then
1727begin
1728Result := BigInteger.Pow(BigInteger.Ten, N);
1729PowersOfTen[N] := Result;
1730end;
1731end;
1732end;
1733
1734class operator BigDecimal.GreaterThan(const Left, Right: BigDecimal): Boolean;
1735begin
1736Result := Compare(Left, Right) > 0;
1737end;
1738
1739class operator BigDecimal.GreaterThanOrEqual(const Left, Right: BigDecimal): Boolean;
1740begin
1741Result := Compare(Left, Right) >= 0;
1742end;
1743
1744class operator BigDecimal.Implicit(const S: Single): BigDecimal;
1745begin
1746Result.Create(S);
1747end;
1748
1749class operator BigDecimal.Implicit(const D: Double): BigDecimal;
1750begin
1751Result.Create(D);
1752end;
1753
1754{$IFDEF HasExtended}
1755class operator BigDecimal.Implicit(const E: Extended): BigDecimal;
1756begin
1757Result.Create(E);
1758end;
1759{$ENDIF}
1760
1761class operator BigDecimal.Implicit(const S: string): BigDecimal;
1762begin
1763Result.Create(S);
1764end;
1765
1766class operator BigDecimal.Implicit(const U: UInt64): BigDecimal;
1767begin
1768Result.Create(U);
1769end;
1770
1771class operator BigDecimal.Implicit(const I: Int64): BigDecimal;
1772begin
1773Result.Create(I);
1774end;
1775
1776class operator BigDecimal.Implicit(const U: UInt32): BigDecimal;
1777begin
1778Result.Create(U);
1779end;
1780
1781class operator BigDecimal.Implicit(const I: Int32): BigDecimal;
1782begin
1783Result.Create(I);
1784end;
1785
1786class operator BigDecimal.Implicit(const UnscaledValue: BigInteger): BigDecimal;
1787begin
1788Result.Create(UnscaledValue);
1789end;
1790
1791procedure BigDecimal.Init;
1792begin
1793FScale := 0;
1794FPrecision := 0;
1795end;
1796
1797{$IFDEF HasClassConstructors}
1798class constructor BigDecimal.InitClass;
1799{$ELSE}
1800class procedure BigDecimal.InitClass;
1801{$ENDIF}
1802var
1803I: Integer;
1804B: BigInteger;
1805begin
1806SetLength(PowersOfTen, 64);
1807B := BigInteger.One;
1808for I := Low(PowersOfTen) to High(PowersOfTen) do
1809begin
1810PowersOfTen[I] := B;
1811B := B * BigInteger.Ten;
1812end;
1813
1814// My default. More or less arbitrary.
1815BigDecimal.FDefaultPrecision := 64;
1816
1817// The most used rounding mode in Delphi, AFAIK.
1818BigDecimal.FDefaultRoundingMode := rmNearestEven;
1819
1820// Reduce trialing zeros to target scale after division by default.
1821BigDecimal.FReduceTrailingZeros := True;
1822
1823// I prefer the lower case 'e', because it is more visible between a number of decimal digits.
1824// IOW, the 'e' in 1.23456789e+345 has, IMO, a little higher visibility than in the 'E' in 1.23456789E+345
1825BigDecimal.FExponentDelimiter := 'e';
1826
1827// The usual constants.
1828BigDecimal.FMinusOne := BigDecimal.Create(BigInteger.MinusOne, 0);
1829BigDecimal.FZero := BigDecimal.Create(BigInteger.Zero, 0);
1830BigDecimal.FOne := BigDecimal.Create(BigInteger.One, 0);
1831BigDecimal.FTwo := BigDecimal.Create(BigInteger(2), 0);
1832BigDecimal.FTen := BigDecimal.Create(BigInteger.Ten, 0);
1833BigDecimal.FHalf := BigDecimal.Create(BigInteger(5), 1);
1834BigDecimal.FOneTenth := BigDecimal.Create(BigInteger(1), 1);
1835
1836///////////////////////////////////////////////////////////////////////////////////////////////
1837// Note: one might expect constants like pi or e, but since BigDecimal relies on a certain //
1838// precision, there can be no constants for such values. The coming BigDecimalMath unit will //
1839// however contain functions to determine them to a given precision. //
1840///////////////////////////////////////////////////////////////////////////////////////////////
1841
1842end;
1843
1844function BigDecimal.Int: BigDecimal;
1845begin
1846Result := RoundToScale(0, rmDown);
1847end;
1848
1849class operator BigDecimal.IntDivide(const Left, Right: BigDecimal): BigDecimal;
1850var
1851LTargetScale: Integer;
1852LRequiredPrecision: Integer;
1853begin
1854LTargetScale := Left.FScale - Right.FScale;
1855if Left.Abs < Right.Abs then
1856begin
1857Result.FValue := BigInteger.Zero;
1858Result.FScale := LTargetScale;
1859Exit;
1860end;
1861
1862if Left.FValue.IsZero and not Right.FValue.IsZero then
1863Exit(Left.RoundToScale(LTargetScale, rmUnnecessary));
1864
1865LRequiredPrecision := RangeCheckedScale(Left.Precision + 3 * Right.Precision + System.Abs(LTargetScale) + 3);
1866Result := Divide(Left, Right, LRequiredPrecision, rmDown);
1867
1868if Result.FScale > 0 then
1869begin
1870Result := Result.RoundToScale(0, rmDown);
1871InPlaceRemoveTrailingZeros(Result, LTargetScale);
1872end;
1873
1874if Result.Scale < LTargetScale then
1875Result := Result.RoundToScale(LTargetScale, rmUnnecessary);
1876end;
1877
1878class function BigDecimal.IntPower(const Base: BigDecimal; Exponent, Precision: Integer): BigDecimal;
1879var
1880LBase: BigDecimal;
1881LNegativeExp: Boolean;
1882begin
1883if Exponent = 0 then
1884Exit(BigDecimal.One);
1885
1886LNegativeExp := Exponent < 0;
1887if LNegativeExp then
1888Exponent := -Exponent;
1889
1890if Exponent > 9999999 then
1891Error(ecExponent, []);
1892
1893if (Base.Precision > 8) and (Exponent >= IntPowerExponentThreshold) then
1894begin
1895Result := One;
1896LBase := Base;
1897while Exponent <> 0 do
1898begin
1899if Odd(Exponent) then
1900Result := (Result * LBase).RoundToPrecision(Precision + 3);
1901LBase := (LBase * LBase).RoundToPrecision(Precision + 3);
1902Exponent := Exponent shr 1;
1903end;
1904end
1905else
1906Result := IntPower(Base, Exponent);
1907
1908if LNegativeExp then
1909Result := Result.Reciprocal(Precision)
1910else
1911Result := Result.RoundToPrecision(Precision);
1912
1913if Result.Scale < Precision then
1914Result := Result.RemoveTrailingZeros(0);
1915end;
1916
1917class function BigDecimal.IntPower(const Base: BigDecimal; Exponent: Integer): BigDecimal;
1918var
1919LBase: BigDecimal;
1920LNegativeExp: Boolean;
1921begin
1922
1923if Exponent = 0 then
1924Exit(BigDecimal.One);
1925
1926LNegativeExp := Exponent < 0;
1927if LNegativeExp then
1928Exponent := -Exponent;
1929
1930if Exponent > 9999999 then
1931Error(ecExponent, []);
1932
1933Result := One;
1934LBase := Base;
1935while Exponent <> 0 do
1936begin
1937if Odd(Exponent) then
1938Result := Result * LBase;
1939LBase := LBase * LBase;
1940Exponent := Exponent shr 1;
1941end;
1942
1943if LNegativeExp then
1944Result := BigDecimal.Divide(BigDecimal.One, Result, DefaultPrecision);
1945end;
1946
1947function BigDecimal.IntPower(Exponent, Precision: Integer): BigDecimal;
1948begin
1949Result := IntPower(Self, Exponent, Precision);
1950end;
1951
1952function BigDecimal.IntPower(Exponent: Integer): BigDecimal;
1953begin
1954Result := IntPower(Self, Exponent);
1955end;
1956
1957function BigDecimal.IsZero: Boolean;
1958begin
1959Result := FValue.IsZero;
1960end;
1961
1962function BigDecimal.IsPositive: Boolean;
1963begin
1964Result := FValue.IsPositive;
1965end;
1966
1967function BigDecimal.IsNegative: Boolean;
1968begin
1969Result := FValue.IsNegative;
1970end;
1971
1972class operator BigDecimal.LessThan(const left, Right: BigDecimal): Boolean;
1973begin
1974Result := Compare(Left, Right) < 0;
1975end;
1976
1977class operator BigDecimal.LessThanOrEqual(const Left, Right: BigDecimal): Boolean;
1978begin
1979Result := Compare(Left, Right) <= 0;
1980end;
1981
1982class function BigDecimal.Max(const Left, Right: BigDecimal): BigDecimal;
1983begin
1984if Compare(Left, Right) > 0 then
1985Result := Left
1986else
1987Result := Right;
1988end;
1989
1990class function BigDecimal.Min(const Left, Right: BigDecimal): BigDecimal;
1991begin
1992if Compare(Left, Right) < 0 then
1993Result := Left
1994else
1995Result := Right;
1996end;
1997
1998class operator BigDecimal.Modulus(const Left, Right: BigDecimal): BigDecimal;
1999begin
2000Result := Remainder(Left, Right);
2001end;
2002
2003function BigDecimal.MovePointLeft(Digits: Integer): BigDecimal;
2004var
2005NewScale: Integer;
2006begin
2007NewScale := RangeCheckedscale(Scale + Digits);
2008Result := BigDecimal.Create(FValue, NewScale);
2009if Result.FScale < 0 then
2010Result.FScale := 0;
2011end;
2012
2013function BigDecimal.MovePointRight(Digits: Integer): BigDecimal;
2014var
2015NewScale: Integer;
2016begin
2017NewScale := RangeCheckedScale(Scale - Digits);
2018Result := BigDecimal.Create(FValue, NewScale);
2019if Result.FScale < 0 then
2020Result.FScale := 0;
2021end;
2022
2023class operator BigDecimal.Multiply(const Left, Right: BigDecimal): BigDecimal;
2024begin
2025Result := Multiply(Left, Right);
2026end;
2027
2028///////////////////////////////////////////////////////////////////////////////////////
2029// //
2030// Multiplication is the easiest: multiply the unscaled values and add the scales. //
2031// //
2032///////////////////////////////////////////////////////////////////////////////////////
2033
2034class function BigDecimal.Multiply(const Left, Right: BigDecimal): BigDecimal;
2035begin
2036Result.Init;
2037Result.FScale := RangeCheckedScale(Left.FScale + Right.FScale);
2038Result.FValue := Left.FValue * Right.FValue;
2039end;
2040
2041class function BigDecimal.Negate(const Value: BigDecimal): BigDecimal;
2042begin
2043Result.Init;
2044Result.FValue := -Value.FValue;
2045Result.FScale := Value.FScale;
2046end;
2047
2048class operator BigDecimal.Negative(const Value: BigDecimal): BigDecimal;
2049begin
2050Result := Negate(Value);
2051end;
2052
2053class operator BigDecimal.NotEqual(const Left, Right: BigDecimal): Boolean;
2054begin
2055Result := Compare(left, Right) <> 0;
2056end;
2057
2058class function BigDecimal.Parse(const S: string; const Settings: TFormatSettings): BigDecimal;
2059begin
2060Result.Init;
2061if not TryParse(S, Settings, Result) then
2062Error(ecParse, [S, 'BigDecimal']);
2063end;
2064
2065class function BigDecimal.Parse(const S: string): BigDecimal;
2066begin
2067Result.Init;
2068if not TryParse(S, Result) then
2069Error(ecParse, [S, 'BigDecimal']);
2070end;
2071
2072class operator BigDecimal.Positive(const Value: BigDecimal): BigDecimal;
2073begin
2074Result.Init;
2075Result := Value;
2076end;
2077
2078function BigDecimal.Precision: Integer;
2079type
2080CardRec = packed record
2081Lo: Cardinal;
2082Hi: Integer;
2083end;
2084const
2085// 1292913986 is Log10(2) * 1^32.
2086CMultiplier = Int64(1292913986);
2087var
2088Full: Int64;
2089begin
2090Result := FPrecision;
2091if Result = 0 then
2092begin
2093//Note: Both 9999 ($270F) and 10000 ($2710) have a bitlength of 14, but 9999 has a precision of 4, while 10000 has a precision of 5.
2094// In other words: BitLength is not a good enough measure for precision. The test with the power of ten is necessary.
2095Full := Int64(FValue.BitLength + 1) * CMultiplier;
2096Result := CardRec(Full).Hi;
2097if (GetPowerOfTen(Result) <= Abs(FValue)) or (Result = 0) then
2098Inc(Result);
2099FPrecision := Result;
2100end;
2101end;
2102
2103// Checks new scale for over- or underflow. Returns new scale.
2104class function BigDecimal.RangeCheckedScale(NewScale: Int32): Integer;
2105begin
2106if NewScale > MaxScale then
2107Error(ecUnderflow, [])
2108else if NewScale < MinScale then
2109Error(ecOverflow, []);
2110Result := NewScale;
2111end;
2112
2113function BigDecimal.Reciprocal(Precision: Integer): BigDecimal;
2114begin
2115Result := Divide(BigDecimal.One, Self, Precision, DefaultRoundingMode);
2116end;
2117
2118function BigDecimal.Reciprocal: BigDecimal;
2119begin
2120Result := Divide(BigDecimal.One, Self, DefaultPrecision, DefaultRoundingMode);
2121end;
2122
2123class function BigDecimal.Remainder(const Left, Right: BigDecimal): BigDecimal;
2124var
2125LQuotient: BigDecimal;
2126begin
2127Result.Init;
2128LQuotient := Left div Right;
2129Result := Left - LQuotient * Right;
2130end;
2131
2132function BigDecimal.RemoveTrailingZeros(TargetScale: Integer): BigDecimal;
2133begin
2134Result := Self;
2135if (TargetScale >= FScale) or (Precision = 1) then
2136Exit;
2137FPrecision := 0;
2138InPlaceRemoveTrailingZeros(Result, TargetScale);
2139end;
2140
2141class function BigDecimal.Round(const Value: BigDecimal): Int64;
2142begin
2143Result := Round(Value, DefaultRoundingMode);
2144end;
2145
2146class function BigDecimal.Round(const Value: BigDecimal; ARoundingMode: RoundingMode): Int64;
2147var
2148Rounded: BigDecimal;
2149begin
2150Result := 0;
2151Rounded := Value.RoundTo(0, ARoundingMode);
2152try
2153Result := Rounded.FValue.AsInt64;
2154except
2155Error(ecConversion, ['BigDecimal', 'Int64']);
2156end;
2157end;
2158
2159class operator BigDecimal.Round(const Value: BigDecimal): Int64;
2160begin
2161Result := BigDecimal.Round(Value);
2162end;
2163
2164function BigDecimal.RoundTo(Digits: Integer): BigDecimal;
2165begin
2166Result := RoundToScale(-Digits, DefaultRoundingMode);
2167end;
2168
2169function BigDecimal.RoundTo(Digits: Integer; ARoundingMode: RoundingMode): BigDecimal;
2170begin
2171Result := RoundToScale(-Digits, ARoundingMode);
2172end;
2173
2174function BigDecimal.RoundToPrecision(APrecision: Integer): BigDecimal;
2175var
2176PrecisionDifference: Integer;
2177begin
2178PrecisionDifference := APrecision - Self.Precision;
2179Result := RoundTo(-(Scale + PrecisionDifference));
2180end;
2181
2182///////////////////////////////////////////////////////////////////////////////////////////////////////
2183// //
2184// Note: //
2185// Rounding is done in several parts of this unit. Instead of using the classic bitwise //
2186// methodology (guard, round and sticky bits), I like to use the remainder after a //
2187// division by a power of ten. //
2188// //
2189// Division truncates, so the first four rounding modes, rmDown, rmUp, rmCeiling and //
2190// rmFloor are easy: truncate and then look if you must add one to the quotient, depending //
2191// on these rounding modes only (and on the sign). //
2192// //
2193// But the next three, rmNearestUp, rmNearestDown and rmNearestEven, depend on whether the //
2194// remainder is "half" of the low bit. That is how the remainder is used: if //
2195// remainder + remainder > divisor, we must round up, if it is < divisor we must round down, //
2196// and if = divisor, the rounding mode determines if the quotient must be incremented or not. //
2197// //
2198// This principle is used throughout this unit. //
2199// //
2200///////////////////////////////////////////////////////////////////////////////////////////////////////
2201
2202function BigDecimal.RoundToScale(NewScale: Integer; ARoundingMode: RoundingMode): BigDecimal;
2203var
2204LScaleDifference: Integer;
2205LValue, LDivisor: BigInteger;
2206LRemainder, LQuotient: BigInteger;
2207LSign: Integer;
2208begin
2209Result.Init;
2210LScaleDifference := RangeCheckedScale(Self.Scale - NewScale);
2211if LScaleDifference > 0 then
2212begin
2213LDivisor := GetPowerOfTen(LScaleDifference);
2214LSign := FValue.Sign;
2215LValue := BigInteger.Abs(FValue);
2216BigInteger.DivMod(LValue, LDivisor, LQuotient, LRemainder);
2217AdjustForRoundingMode(LQuotient, LDivisor, LRemainder, LSign, ARoundingMode);
2218Result.FValue := LSign * LQuotient;
2219end
2220else if LScaleDifference < 0 then
2221Result.FValue := Self.FValue * GetPowerOfTen(-LScaleDifference)
2222else
2223Result.FValue := Self.FValue;
2224Result.FScale := NewScale;
2225end;
2226
2227class procedure BigDecimal.SetExponentDelimiter(const Value: Char);
2228begin
2229if (Value = 'e') or (Value = 'E') then
2230FExponentDelimiter := Value;
2231end;
2232
2233function BigDecimal.Sign: TValueSign;
2234begin
2235Result := FValue.Sign;
2236end;
2237
2238class function BigDecimal.Sqr(const Value: BigDecimal): BigDecimal;
2239begin
2240Result.Init;
2241Result.FValue := BigInteger.Sqr(Value.FValue);
2242Result.FScale := RangeCheckedScale(Value.FScale + Value.FScale);
2243end;
2244
2245function BigDecimal.Sqr: BigDecimal;
2246begin
2247Result := BigDecimal.Sqr(Self);
2248end;
2249
2250class function BigDecimal.Sqrt(const Value: BigDecimal; Precision: Integer): BigDecimal;
2251begin
2252Result := Value.Sqrt(System.Math.Max(Precision, DefaultPrecision));
2253end;
2254
2255class function BigDecimal.Sqrt(const Value: BigDecimal): BigDecimal;
2256begin
2257Result := Value.Sqrt(System.Math.Max(DefaultPrecision, Value.Precision));
2258end;
2259
2260function BigDecimal.Sqrt(Precision: Integer): BigDecimal;
2261var
2262LMultiplier: Integer;
2263LValue: BigInteger;
2264begin
2265// Note: the following self-devised algorithm works. I don't yet know if it can be optimized.
2266// With "works", I mean that if A := B.Sqrt, then (A*A).RoundToScale(B.Scale) = B.
2267Result.Init;
2268Precision := System.Math.Max(Precision, 2 * Self.Precision);
2269
2270// Determine a suitable factor to multiply FValue by to get a useful precision
2271LMultiplier := RangeCheckedScale(Precision - Self.Precision + 1);
2272if Odd(LMultiplier + Self.Scale) then
2273Inc(LMultiplier);
2274
2275// If the multiplier > 0, then multiply BigInteger by 10^LMultiplier
2276if LMultiplier > 0 then
2277LValue := Self.FValue * GetPowerOfTen(LMultiplier)
2278else
2279LValue := Self.FValue;
2280
2281// Using BigInteger.Sqrt should already be pretty close to the desired result.
2282Result.FValue := BigInteger.Sqrt(LValue);
2283Result.FScale := RangeCheckedScale(Self.Scale + LMultiplier) div 2;
2284
2285// Round the result and remove any unnecessary trailing zeroes.
2286Result := Result.RoundToScale(RangeCheckedScale(Result.FScale + Precision div 2 - Result.Precision + 1), DefaultRoundingMode);
2287InPlaceRemoveTrailingZeros(Result, System.Math.Min(Self.Scale, Self.Scale div 2));
2288end;
2289
2290function BigDecimal.Sqrt: BigDecimal;
2291begin
2292Result := Self.Sqrt(DefaultPrecision);
2293end;
2294
2295class function BigDecimal.Subtract(const Left, Right: BigDecimal): BigDecimal;
2296var
2297A, B: BigInteger;
2298begin
2299Result.Init;
2300if Left.Scale > Right.Scale then
2301begin
2302A := Left.FValue;
2303
2304// There is no need to use RangeCheckedScale, because one scale is simply changed to the other, and both
2305// were already in range.
2306B := Right.FValue * GetPowerOfTen(Left.Scale - Right.Scale);
2307Result.FScale := Left.Scale;
2308end
2309else
2310begin
2311A := Left.FValue * GetPowerOfTen(Right.Scale - Left.Scale);
2312B := Right.FValue;
2313Result.FScale := Right.Scale;
2314end;
2315Result.FValue := A - B;
2316end;
2317
2318class operator BigDecimal.Subtract(const Left, Right: BigDecimal): BigDecimal;
2319begin
2320Result := Subtract(Left, Right);
2321end;
2322
2323// Returns decimal notation (i.e. without using exponents).
2324function BigDecimal.ToPlainString(const Settings: TFormatSettings): string;
2325var
2326S: string;
2327LNegative: Boolean;
2328LScale, LLength: Integer;
2329begin
2330LNegative := FValue.IsNegative;
2331S := BigInteger.Abs(FValue).ToString(10);
2332LScale := Self.Scale;
2333LLength := Length(S);
2334if LScale < 0 then
2335Result := S + StringOfChar('0', -LScale)
2336else if LScale = 0 then
2337Result := S
2338else if LScale >= LLength then
2339Result := '0' + Settings.DecimalSeparator + StringOfChar('0', LScale - LLength) + S
2340else
2341Result := Copy(S, 1, LLength - LScale) + Settings.DecimalSeparator + Copy(S, LLength - LScale + 1, MaxInt);
2342if LNegative then
2343Result := '-' + Result;
2344end;
2345
2346function BigDecimal.ToPlainString: string;
2347begin
2348Result := ToPlainString(InvariantSettings);
2349end;
2350
2351function BigDecimal.ToString: string;
2352begin
2353Result := ToString(InvariantSettings);
2354end;
2355
2356function BigDecimal.ToString(const Settings: TFormatSettings): string;
2357var
2358AdjustedExponent: Integer;
2359PlainText: string;
2360Negative: Boolean;
2361begin
2362Negative := FValue.IsNegative;
2363PlainText := BigInteger.Abs(FValue).ToString(10);
2364AdjustedExponent := Length(PlainText) - 1 - Self.Scale;
2365if (Self.Scale >= 0) and (AdjustedExponent >= -6) then
2366Result := ToPlainString(Settings)
2367else
2368begin
2369// Exponential notation
2370if Length(PlainText) > 1 then
2371PlainText := PlainText[1] + Settings.DecimalSeparator + Copy(PlainText, 2, MaxInt);
2372PlainText := PlainText + FExponentDelimiter;
2373if AdjustedExponent >= 0 then
2374PlainText := PlainText + '+';
2375PlainText := PlainText + IntToStr(AdjustedExponent);
2376if Negative then
2377PlainText := '-' + PlainText;
2378Result := PlainText;
2379end;
2380end;
2381
2382function BigDecimal.Trunc: Int64;
2383var
2384Rounded: BigDecimal;
2385begin
2386Result := 0; // Avoid warning.
2387Rounded := Self.RoundTo(0, rmDown);
2388try
2389Result := Rounded.FValue.AsInt64;
2390except
2391Error(ecConversion, ['BigDecimal', 'Int64']);
2392end;
2393end;
2394
2395class operator BigDecimal.Trunc(const Value: BigDecimal): Int64;
2396begin
2397Result := Value.Trunc;
2398end;
2399
2400// Converts string with national settings to invariant string and then calls TryParse(string, BigDecimal).
2401class function BigDecimal.TryParse(const S: string; const Settings: TFormatSettings; out Value: BigDecimal): Boolean;
2402var
2403InvariantString: string;
2404I: Integer;
2405begin
2406SetLength(InvariantString, Length(S));
2407for I := 1 to Length(S) do
2408begin
2409if S[I] = Settings.DecimalSeparator then
2410InvariantString[I] := '.'
2411else if S[I] = Settings.ThousandSeparator then
2412InvariantString[I] := ','
2413else
2414InvariantString[I] := S[I];
2415end;
2416Result := TryParse(InvariantString, Value);
2417end;
2418
2419class function BigDecimal.TryParse(const S: string; out Value: BigDecimal): Boolean;
2420var
2421LIsNegative: Boolean;
2422LIsNegativeExponent: Boolean;
2423LExponent: Integer;
2424LNumDecimals: Integer;
2425LDecimalPointPos: PChar;
2426LTrimmedS: string;
2427LPtr: PChar;
2428LChr: Char;
2429LIntValue: string;
2430begin
2431Value.Init;
2432Result := False;
2433LIntValue := '';
2434LTrimmedS := Trim(S);
2435LPtr := PChar(LTrimmedS);
2436if LPtr^ = #0 then
2437Exit;
2438LIsNegative := False;
2439LIsNegativeExponent := False;
2440LDecimalPointPos := nil;
2441if (LPtr^ = '+') or (LPtr^ = '-') then
2442begin
2443LIsNegative := (LPtr^ = '-');
2444Inc(LPtr);
2445end;
2446if LPtr^ = #0 then
2447Exit;
2448Value.FValue := BigInteger.Zero;
2449LNumDecimals := 0;
2450
2451// Parse text up to any exponent.
2452LChr := LPtr^;
2453while (LChr <> #0) and (LChr <> 'e') and (LChr <> 'E') do // DO NOT TRANSLATE!
2454begin
2455case LChr of
2456'0'..'9':
2457LIntValue := LIntvalue + LChr;
2458',':
2459; // Ignore thousand-separators.
2460'.':
2461if Assigned(LDecimalPointPos) then
2462// Decimal point was parsed already, so exit indicating invalid result.
2463Exit
2464else
2465LDecimalPointPos := LPtr;
2466else
2467Exit;
2468end;
2469Inc(LPtr);
2470LChr := LPtr^;
2471end;
2472
2473// Parsed significand to end or up to first 'e' or 'E'.
2474if Assigned(LDecimalPointPos) then
2475LNumDecimals := LPtr - LDecimalPointPos - 1;
2476
2477LExponent := 0;
2478if (LChr = 'e') or (LChr = 'E') then // DO NOT TRANSLATE!
2479begin
2480// Parse exponent
2481Inc(LPtr);
2482if (LPtr^ = '+') or (LPtr^ = '-') then
2483begin
2484LIsNegativeExponent := (LPtr^ = '-');
2485Inc(LPtr);
2486end;
2487while LPtr^ <> #0 do
2488begin
2489case LPtr^ of
2490'0'..'9':
2491LExponent := LExponent * 10 + Ord(LPtr^) - Ord('0');
2492else
2493Exit;
2494end;
2495Inc(LPtr);
2496end;
2497end;
2498if LIsNegativeExponent then
2499LExponent := -LExponent;
2500LNumDecimals := LNumDecimals - LExponent;
2501
2502Value.FScale := LNumDecimals;
2503Value.FValue := BigInteger(LIntValue);
2504if not Value.FValue.IsZero and LIsNegative then
2505Value.FValue.SetSign(-1);
2506Result := True;
2507end;
2508
2509function BigDecimal.ULP: BigDecimal;
2510begin
2511Result.FPrecision := 1;
2512Result.FValue := BigInteger.One;
2513Result.FScale := Self.Scale;
2514end;
2515
2516{$IFNDEF HasClassConstructors}
2517initialization
2518BigDecimal.InitClass;
2519{$ENDIF}
2520
2521end.
2522