Delphi/Kylix Math Info

Contents

A. Delphi/Kylix Math Unit   efg's Delphi Math Resources
B. Floating-Point Numbers, IEEE Math efg's Delphi Math Functions
C. Delphi Math Tips and Tricks efg's Mathematics Page

A. Delphi/Kylix Math Unit

Question Answer
A-1. Where is the Delphi Math unit? The math unit is included in all versions of Delphi 4/5/6 and Kylix. The Delphi Math unit is available only with the Professional or Client/Server versions of Delphi 3 -- it is not available with the "Standard" version. It is also available with the Developer version of Delphi 2 and with the Client/Server versions of Delphi 1 or 2. You'll find the source code unit in directory ..\source\rtl\sys\math.pas.
A-2.  What's new in the Delphi/Kylix  Math unit?
D6 The D6 math unit has everything listed next under Kylix, plus
  • a SimpleRoundTo function.  
  • VarCmplx unit defines a number of complex math functions (find the source in ..\Delphi6\Source\Rtl\Common):

General:  VarComplexCreate (with overloads), VarComplex, VarIsComplex, VarAsComplex, VarComplexSimplify, VarComplexAbsSqr, VarComplexAbs, VarComplexAngle, VarComplexConjugate, VarComplexInverse, VarComplexExp, VarComplexLn, VarComplexSqr, VarComplexSqrt, VarComplexTimesPosI, VarComplexTimesNegI, VarComplexPower

Trig/Hyperbolic:  VarComplexCos, VarComplexSin, VarComplexTan, VarComplexCot, VarComplexSec, VarComplexCsc, VarComplexArcCos, VarComplexArcSin, VarComplexArcTan, VarComplexArcCot, VarComplexArcSec, VarComplexArcCsc, VarComplexCosH, VarComplexSinH, VarComplexTanH, VarComplexCotH, VarComplexSecH, VarComplexCscH, VarComplexArcCosH, VarComplexArcSinH, VarComplexArcTanH, VarComplexArcCotH, VarComplexArcSecH,  VarComplexArcCscH

Conversion:  VarComplexToPolar, VarComplexFromPolar

Variables:  ComplexNumberSymbol, ComplexNumberSymbolBeforeImaginary

  • FMTBcd  unit defines a umber of binary-coded decimal (BCD) routines (find the source in ..\Delphi6\Source\Vcl):

Types:   PBcd, TBcd
Const:  NullBcd
Exceptions:  EBcdException, EBcdOverflowException

Utility:  BcdPrecision, BcdScale, IsBcdNegative

Arithmetic:  BcdAdd, BcdSubtract, NormalizeBcd, BcdMultiply (8 overloads)

Creation:  VarFMTBcdCreate (5 overloads), VarIsFMTBcd, VarFMTBcd

Conversions:  StrToBcd, TryStrToBcd, DoubleToBcd, DoubleToBcd, IntegerToBcd, VarToBcd, CurrToBCD, BcdToStr, BcdToDouble, BcdToInteger, BCDToCurr, BcdToStrF, FormatBcd, BcdCompare 

In addition, these minor changes were made to the D6 math unit:

  • A FLD1 assembly statement was added to the Secant and Cosecant functions just prior to the FDIVRP statement.
  • In Kylix, the math unit defined TValueRelationship, LessThanValue, EqualsValue and GreaterThanValue.  This type and constants were moved to the Types unit in D6.
  • The EnsureRange overloads (Integer, Int64, Double) all had the statement "assert (AMin <= aMax);" that was not present in the Kylix code. 
  • In Kylix, the SumInt, Sum, and SumOfSquares functions had a section that was conditionally compiled IFDEF PIC.  These sections were Delphi code and were dropped.  The ELSE section from Kylix, which was in assembly language, was all that was present in D6.
  • The GNU license statement was present in the Kylix math unit but was removed in D6.
  • Several cosmetic changes were made to align code and wrap comments in D6 compared to the Kylix version.  Some dead code (namely the AnnuityF function that was commented out in Kylix) was dropped in D6.
Kylix The  Kylix math unit has everything below in A-3, plus the following:

Constants for NaN (not-a-number), Infinity, NegInfinity
Trig: Secant or Sec, Cosecant or Csc, Cot, ArcSec, ArcCsc, ArcCot, 
Angles:  DegToGrad, DegToCycle, GradToDeg, GradToCycle, CycleToDeg, CycleToGrad
Hyperbolic: CotH, SecH, CscH, ArcCotH, ArcSecH, ArcCscH

Miscellaneous:  

IsNan, IsInfinite, Sign overloads, CompareValue overloads, SameValue overloads, IsZero overlaods, IfThen overloads, InRange overloads, EnsureRange overloads, DivMod, RoundTo, GetRoundMode, SetRoundMode, GetPrecisionMode, SetPrecisionMode, GetExceptionMask, SetExceptionMask, ClearExceptions

Type:  TValueSign, TValueRelationship, TRoundToRange, TFPURoundingRange, TFPUException, TFPUExceptionMask

Const:  NegativeValue, ZeroValue, PositiveValue, LessThanValue, EqualsValue, GreaterThanValue

Statistical: RandomRange, RandomFrom overloads

D5 Only the copyright date was changed in D5.  There are no changes of any kind in the D5 math unit compared to the D4 math unit.
D4
  • Overloaded Min(A,B) and Max(A,B) functions for types Integer, Int64, Single, Double, Extended.
  • Each of the functions Ceil, Floor and Power has the construct Integer(Trunc(X)) instead of Trunc(X)
D3 Open array functions MinIntValue, MaxIntValue, SumInt
According to Borland, "Some Math unit financial functions [were] corrected."
A-3. What functions are in the Delphi 32 (i.e., Delphi 2 - 4) Math unit?

(Note: [n] indicates function was new in Delphi n)
Min and Max constants for Single, Double, Extended, Comp types
Trig: ArcCos, ArcSin, ArcTan2, SinCos, Tan, CoTan, Hypot
Angles: DegToRad, RadToDeg, GradToRad, RadToGrad, CycleToRad, RadToCycle
Hyperbolic: Cosh, Sinh, Tanh, ArcCosh, ArcSinh, ArcTanh
Logarithm: LnXP1, Log10, Log2, LogN
Exponential: IntPower, Power
Miscellaneous: Frexp, Ldexp, Ceil, Floor, Poly, EInvalidArgument type
Statistical: Mean, Sum, SumInt [3], SumOfSquares, SumsAndSquares, MinValue, MinIntValue [3], Min overloads [4], MaxValue, MaxIntValue [3], Max  overloads[4], StdDev, MeanAndStdDev, PopnStdDev, Variance, PopnVariance, TotalVariance, Norm, MomentSkewKurtosis, RandG
Financial: TPaymentTIme type, DoubleDecliningBalance, FutureValue, InterestPayment, InterestRate, InternalRateOfReturn, NumberOfPeriods, NetPresentValue, Payment, PeriodPayment, PresentValue, SLNDepreciation, SYDDepreciation

Note:
The function definitions for each of these functions may be found in the Delphi Math Functions Pages.

Chapter 9, "The Shadowy Math Unit" in Kick Ass Delphi Programming

A-4.  Where can I find other Delphi/ Pascal math function libraries? Delphi Math Functions Pages
Source of Numerical Analysis Code in Pascal
Jean Debord's TPMath page
ESB Consultancy's ESBMaths
Razor's Edge Software MathX
SDL's (Software Development Lohninger) Math1 and Math2

B. Floating-Point Numbers, IEEE Math

Question Answer
B-1.  Overview Info What Every Computer Scientist Should Know About Floating-Point Arithmetic
http://docs.sun.com/source/806-3568/ncg_goldberg.html 
B-2. What are the Object Pascal numeric ordinal types?
Type Delphi
Version
Windows Unit
Type
Min Value Max Value Signed Bytes
ShortInt all   -128 127 Yes 1
Byte all UCHAR 0 255 No 1
SmallInt all SHORT -32768 32767 Yes 2
Word all   0 65535 No 2
LongWord 4-6 DWORD,
UINT
0 232 - 1 =
4,294,967,295
No 4
LongInt all   -231 =
-2,147,483,648
231 - 1 =
2,147,483,647
Yes 4
Int64
4-6 LongLong
TLargeInteger*
LARGE_INTEGER*
-263 263 - 1 Yes 8
- 4-5 TULargeInteger
ULARGE_INTEGER*
0 264 - 1 No 8
Integer
(generic)
1   -32768 32767 Yes 2
2-6   -2,147,483,648 2,147,483,647 Yes 4
Cardinal
(generic)
1   0 65535 No 2
2-3   0 2,147,483,647 No 4
4-6 ULONG 0 4,294,967,295 No 4

Notes:

  • Use generic types whenever possible for better performance.
  • See "Comp" below in B-3 for an alternative to Int64 especially for D1-D3.
  • In general, arithmetic operations on integers return a value of type Integer -- which, in Delphi 2-5, is equivalent to the 32-bit LongInt. Operations in Delphi 4-5 return a value of type Int64 only when performed on an Int64 operand. So if I is an Integer and J is an Int64, use the statement J := Int64(I) + 1 for correct results.
  • An integer-type value can be explicitly converted to another integer type through typecasting.
  • Most standard routines that take integer arguments truncate Int64 values to 32 bits. However, the High, Low, Succ, Pred, Inc, Dec, IntToStr, and IntToHex routines fully support Int64 arguments. Also, the Round, Trunc, StrToInt64, and StrToInt64Def functions return Int64 values. A few routines -- including Ord -- cannot take Int64 values at all.
  • *Both Delphi 4 and 5 define a ULARGE_INTEGER variant record that can be viewed as two DWORDs (LowPart and HighPart), or a single LongLong QuadPart.  While a ULARGE_INTEGER can hold an unsigned 64-bit quantitity, there are no known Delphi functions for manipulating these quantities.   The LARGE_INTEGER variant record is just like the ULARGE_INTEGER definition except the HighPart is a LongInt (instead of a DWORD).   In Delphi 3 The TLargeInteger variant record was defined like LARGE_INTEGER in D4/D5.
  • CAUTION: There is at least one problem introduced somehow indirectly by the new Int64 support in Delphi 4. Example: Given variables A and B of type Byte or Word, with A := 40 and B := 45, the expression A - B is -5 as expected (as it has been in Delphi 1, 2 and 3), but (A-B) DIV 4 is 1,073,741,822 (slightly unexpected). There is no compiler warning about this possible consequence. Borland claims this is NOT a bug. In Delphi 4, if A and B are LongWords (32-bit unsigned values), the expression A - B is 4,294,967,291. IMHO, Borland should treat all unsigned quantities in a like way. [Note: See 7/12/98 post "D4 Arithmetic Bug or Feature when subtracting byte values?" to various Delphi newsgroups and replies for some additional details.]  This is fixed in D5:  A - B = -5, and (A - B) DIV 4 = -1, as expected.
  • See Ray Lischner's "Delphi 4 Integers:  The Long and the Short of It" in March/April 1999 Visual Developer, pp. 92-93.
  • Ray Lischner's UseNet Post with reasons why to use Int64 instead of Comp.  Baji Kimran's comment about Ray's reason #1:  "I think the reason for only 18 digits being correct is that the
    FPU has a float to BCD instruction that is used to convert to decimal, and it has a bug for 19 digits."
  • See Marc Palmer's UseNet Post about Int64 range checking problems
  • See Performance of large integer types.  In a UseNet posting, Baji Kimran says "in my experience, if the dividend is > 2^32, Int64 div and mod are pretty slow.  Then a comp / is considerably faster than an Int64 div.  You can even do a mod function on comps faster than int64 mod (if the dividend is >2^32).  If you have a mixture of arithmetic types in a program, the actual mix of operations
    determines whether comp or int64 is faster.  If there are many div or mod operations, comp is usually faster, since they are so slow on int64, > 2^32."

Thanks to Roman Krejci for correcting an error in the above table about the generic integer in Delphi versions 2-3. (2 Aug 1998)

B-3. What are the floating-point data types?
Type Delphi
Version
Min Value Max Value Significant
Digits
Bytes
Real
(generic)
1-3 2.9 x 10-39 1.7 x 1038 11-12 6
4-6 5.0 x 10-324 1.7 x 10308 15-16 8
Real48 4-6 2.9 x 10-39 1.7 x 1038 11-12 6
Single all 1.5 x 10-45 3.4 x 1038 7-8 4
Double all 5.0 x 10-324 1.7 x 10308 15-16 8
Extended all 3.4 x 10-4932 1.1 x 104932 19-20 10
Comp all -263 + 1 or -9.2 x 1018 263 - 1 or 9.2 x 1018 19-20 8
Currency 2-6 -922,337,203,685,477.5808 922,337,203,685,477.5807 19-20 8

Notes:

  • The generic type Real in Delphi 4-5 is equivalent to Double.
  • The 6-byte Real48 type was called Real in earlier versions of Object Pascal. If you are recompiling code that uses the older, 6-byte Real type, you may want to change it to Real48. You can also use the {$REALCOMPATIBILITY ON} compiler directive to turn Real back into the 6-byte type.
  • Real48 is maintained for backward compatibility. Since its storage format is not native to the Intel CPU family, it results in slower performance than other floating-point types.
  • Extended offers greater precision than other real types but is less portable. Be careful using Extended if you are creating data files to share across platforms.
  • The Comp (computational) type is native to the Intel CPU and represents a 64-bit integer. A Comp is classified as a real, however, it does not behave like an ordinal type. Comp is maintained for backward compatibility only. Use the Int64 type for better performance.
  • Currency is a fixed-point data type that minimizes rounding errors in monetary calculations. It is stored as a scaled 64-bit integer with the four least-significant digits implicitly representing decimal places. When mixed with other real types in assignments and expressions, Currency values are automatically divided or multiplied by 10000.
  • Ray Lischner's UseNet Post with reasons why to use Int64 instead of Comp
  • Tips and Tricks from Delphi in a NutShell:
    Double type:  pp. 184-186:  shows how to extract sign, exponent, mantissa;
    Extended type:  pp. 202-204:  shows how to extract sign, exponent, mantissa;
    Single type:  pp. 342-344:  shows how to extract sign, exponent, mantissa;
    Real48 type:  p. 309;
    Comp type:  p. 166;
    Currency type:  p. 173
B-4. How can I learn more about floating-point numbers? See Borland's Tech Info 1027, An Overview of Floating Point Numbers,
http://community.borland.com/article/0,1410,15855,00.html 
(The math in the article is OK, but the programming examples are in C, unfortunately.)

The article "Reviewing Delphi by the numbers" in the April 1998 Delphi Developer's Journal (pp. 7-11) is a good overview of ordinal and floating-point types in Delphi.

B-5. What is IEEE floating point? IEEE-754 is a standard way to store floating point values on a variety of computers. The Single, Double and Extended data types are all stored in IEEE format. The older Real values are not stored in IEEE format.

See efg's NaN Tech Note

IEEE Standard 754 Floating Point Numbers
www.research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html

Formally Verifying IEEE Compliance of Floating-Point Hardware
http://developer.intel.com/technology/itj/q11999/articles/art_5.htm

A German description of IEEE-754, Gleitkommazahlen nach IEEE, is available from http://www.informatik.uni-halle.de/lehre/pascal/sprache/pas_ieee.html.

Future 64-bit Intel Architecture:
The Computation of Transcendental Functions on the IA-64 Architecture
http://developer.intel.com/technology/itj/q41999/articles/art_5.htm

IA-64 Floating-Point Operations and the IEEE Standard for Binary Floating-Point Arithmetic
http://developer.intel.com/technology/itj/q41999/articles/art_6.htm

John Herbster's T_BinaryFloatingPoint program for analyzing the extended, double, and single binary point numbers.

B-6. How can I convert my old Real values to IEEE doubles? See Richard Biffl's BPREAL.C function and related files: BPReal.ZIP

To convert Turbo Pascal 3.0 BCD reals (10-byte binary coded decimal reals with 18 significant digits) to Turbo Pascal 5.0 floating point numbers, download Turbo Pascal 5.5 from the Borland Community Museum:  http://community.borland.com/museum, and install it.  Look for BCD.PAS in the Turbo3 directory.

B-7. How can I convert between Microsoft Binary and IEEE format?  Mac format?

TI1431C, Converting between Microsoft Binary and IEEE format
http://community.borland.com/article/0,1410,16431,00.html  

Mark Di Val's Usenet Post showing Microsoft's conversion functions (in C/C++)

Alex Grigny de Castro's UseNet Post show Mac number float format conversion
Jason Welter's E-Mail instructing how to reverse doubles for Macs

B-8. What is a NAN? How can I test for one? What is an INF value? How can I do arithmetic with NAN and INF values? In IEEE math a NAN is "not a number," i.e., an undefined value. INF is infinity and can be positive or negative.

Sample of easy way to define NAN and INF values:
procedure TForm1.ButtonSpecialClick(Sender: TObject);
  CONST
    Infinity = 1.0/0.0;
    NaN      = 0.0/0.0;
begin
  // INF NAN
  ShowMessage(FloatToStr(Infinity) + ' ' + FloatToStr(NaN))
end;

TI1716, Testing for Not a Number (NaN)
http://community.borland.com/article/0,1410,16716,00.html 

Ray Lischner's math10.zip has some information about NANs and INFs but only compiles in Delphi 1.

See IEEE754.TXT for a short Delphi 3 unit that defines NAN, PositiveInfinity, etc. and shows an example of how to use such values in expressions. (Also includes a DoubleToHex function to display doubles in logical hex format.)

See efg's Exploring Numbers, Not-a-Number, and Infinity, Delphi Developer, October 1998, pp. 9-14

B-9. What is IEEE rounding? efg's Set8087CW and SetRoundMode rounding example.  See Set8087CW for examples.

The default IEEE rounding rule is to round to the nearest even integer. For example:
ROUND(2.5) = 2, but
ROUND(3.5) = 4

This is also called "banker's rounding.

Ray Lischner's math10.zip only compiles in Delphi 1, but has code to control how the rounding is performed by manipulating the control word of the FPU. Look for functions GetRoundMode and SetRoundMode to control rounding with a TRoundMode constant with values rmNearest, rmDown, rmUp, rmTruncate.

To change the rounding mode, see the function Set8087CW in the Miscellaneous Function section of the Delphi General Functions Page.

B-10. How can I round a floating point number in the traditional way: below 0.5 rounds down, 0.5 and above, rounds up? FAQ1814D.txt Explanation (sic) of rounding
http://community.borland.com/article/0,1410,16814,00.html 

See Set8087CW for how to control rounding modes.

B-11. When I do a StrToFloat('1234.5544') I get something like 1234.55440000000003. What is wrong here?
Nothing is "wrong" here. This is just a limitation of floating-point numbers. The actual source of error is called roundoff error. This error is due to the fault of floating-point arithmetic and to the fact that most decimal fractions become repeating fractions in the binary number system. Such numbers cannot be represented in a finite number of bits. The word roundoff is not always accurate in this connection, and comes from the fact that many computers, when cutting off the last digits from a number, "round" the number to its closest equivalent. Some computers do not round but merely cut off the last bits, with the resulting error still properly called roundoff error (as opposed to truncation error, which is the term used when a series expansion is truncated). From Introduction to Numerical Methods, Peter A. Stark, Macmillian Company, 1970.
B-12. I'm trying to compare two floating point numbers that should be equal. Why aren't the two values equal?

TI2139, How to Compare Floating Point Numbers
http://community.borland.com/article/0,1410,17139,00.html 

Numerical Accuracy 101 for Delphi Developers
http://efd.home.mindspring.com/acc101.htm

Typically, you can use relative or absolute comparisons to avoid problems with "fuzz" (a term used with the language APL) with floating-point numbers.

Because of this "fuzz" with floating point numbers, comparison of two calculated floating point numbers should be based on either absolute or relative error.

For an absolute error comparison use something like:
IF ABS(CalculatedValue - TrueValue) < FuzzValue THEN ..., where FuzzValue is application specific.

For a relative error comparison use something like:

IF ABS( (CalculatedValue - TrueValue) / TrueValue ) < AcceptableRelativeError THEN ... 

where AcceptableRelativeError is application specific (and obviously TrueValue <> 0.0).  HOWEVER, this form of comparision can lead to divide-by-zero problems.  An alternative suggested in a UseNet Post by Hans-Bernhard Broeker avoids this problem by using:
IF ABS( CalculatedValue - TrueValue ) < ABS(AcceptableRelativeError*TrueValue) THEN ...

The Delphi 3 math unit performs relative comparisons like this (but it's not exposed in the unit's interface):

FUNCTION RelSmall(X, Y: Extended): Boolean;
  //  Returns True if X is small relative to Y
  CONST
    C1:  Double = 1E-15;
    C2:  Double = 1E-12;
BEGIN
  Result := Abs(X) < (C1 + C2 * Abs(Y))
END;

Ray Lischner has approximate comparison functions in his Math10.ZIP: FltLE, FltLT, FltGE, FltGT, FltEQ, FltNE

Adam Majewski suggests this function to compute machine epsilon (10/7/2005):

function calceps:real; 

{calceps.pas == This function returns the machine EPSILON or floating point tolerance, the smallest positive real number such that 1.0 + EPSILON > 1.0. EPSILON is needed to set various tolerances for different algorithms. While it could be entered as a constant, I prefer to calculate it, since users tend to move software between machines without paying attention to the computing environment. Note that more complete routines exist.

Idan Nof 12 List. 1999 10:00 posts on comp.lang.pascal.borland }

var 
  e,e0: extended; 
  i: integer; 
begin {calculate machine epsilon} 
  e0 := 1;
  i:=0; 
  repeat 
    e0 := e0/2; 
    e := 1+e0;
    i := i+1; 
  until (e=1.0) or (i=50); {note safety check} 
  e0 := e0*2; 
  { Writeln('Machine EPSILON =',e0);} 
  calceps:=e0; 
end; {calceps}

B-13. Explain how a comp type is a floating point type but is a 64-bit integer.

(Note: Use the new Int64 type in Delphi 4 or later)
Since the floating point unit (FPU) deals with this 64-bit integer, the comp type is treated as if it were a floating point type even though it is a 64-bit integer. Delphi's VCL defines a type called TLargeInteger type that has a QuadPart comp type but also a LowPart and HighPart that are LongInts. You can speed up conversion of a comp to an integer type using TLargeInteger. See Borland's  Assigning a comp type to an integer (FAQ 1965D).

In D4 the TLargeInteger is an Int64, but through a pLargeInteger pointer the LowPart, HighPart and QuadPart of the Int64 can still be accessed like in D3. 

In D4 a TULargeInteger  and a pULargeInteger are defined much like the TLargeInteger and pLargeInteger.  The HighPart of a TULargeInteger is a DWORD (i.e., unsigned) while the HighPart of a TLargeInteger is a LongInt (i.e., signed).

While not very intuitive, you must treat a comp type as a float when converting to a string. Specifically, you must use FloatToStr or FormatFloat to convert to a string. Consider the following example:

VAR
  c: TLargeInteger;
  s: STRING;
...
c.QuadPart := MaxComp; // MaxComp defined in Delphi math unit
s := Format('%8X%8X %d %d %f', 
            [c.HighPart, c.LowPart, c.HighPart, c.LowPart, c.QuadPart]);

The resulting string, s, contains the following: 7FFFFFFFFFFFFFFF 2147483647 -1 9.22337203685477581E18

Another example of using TLargeInteger is in Using the WIN API high resolution performance counter (FAQ 2028D).

B-14. How can I get Delphi to perform stronger type checking on user defined types?  For example, how can I define a type that descends from a double but not pass this type to any function expecting a double (without a warning)?

See Borland FAQ 2126D, "Getting stronger type checking"

B-15.  Unsigned 32 Integers for Delphi 2/3 By Ray Lischner.  The Unsigned unit implements unsigned 32-bit integer arithmetic: comparisons, conversions, and division. Freeware. Source code and help file included. In Delphi 1.0, you must also download the FltMath unit, in math10.zip.
B-16.  Floating Point Optimzation Floating Point Optimization Guidelines
www.optimalcode.com/float.htm 

C. Delphi Math Tips and Tricks

Topic Tip or Trick
C-1. Integer to Float Simple assignment:
x := i;
C-2. Integer to String s := IntToStr(i);
or
Format('%6.6d', [1234]) returns '001234'
C-3. Float to Integer TRUNC(3.5) = 3
or ROUND (see "IEEE Rounding" above)
C-4. Float to String Format, FormatFloat or FloatToStr, but also
FloatToDecimal, FloatToStrF, FloatToText, FloatToTextFmt

Examples:
Format('%.3f times %d is %-16s', 
       [Measurement, Value, Key]); // "C" sprintf format specifications
FormatFloat('##0.0', Factor);
FloatToStr(x);
C-5. String To Float; Sting To Integer If d is a double: d := StrToFloat('1234.5544');

If i is an integer: i := StrToInt('1234');
C-6. How can I localize numbers to display correctly in both the U.S. and European formats?

What is the ThousandSeparator?

What is the DecimalSeparator?
VAR
  x: DOUBLE;
  i: INTEGER;
BEGIN
  x := 1234567.8945;

  Button1.Caption := ThousandSeparator;
  ThousandSeparator := ',';
  DecimalSeparator := '.';
  LabelUSA.Caption := FormatFloat('#,###,###.###', x);

  ThousandSeparator := '.';
  DecimalSeparator := ',';
  LabelEuropean.Caption := FormatFloat('#,###,###.###', x);

The resulting strings are (remember IEEE rounding rounds towards an even number):
USA: 1,234,567.894
European: 1.234.567,894


Treat an integer the same way and use FormatFloat to get a ThousandSeparator in an integer string.
C-7. Dynamically allocated arrays (one dimension) Arrays are incredibly useful and crop up in just about every project we create. But “out of the box” they are rather inflexible as you need to decide upfront how big your array should be. Not any more, Brian Long explains how to make your arrays dynamic: resizeable and powerful, for Delphi 1 right through to Delphi 4, with its new built-in dynamic arrays feature.
Delphi Magazine, Issue 37, September 1998

How to Create an Array of 2,000,000 doubles in Delphi 1: Delphi1LargeArray.ZIP

Also, see example below in "Open Array; Slice Function."

I don't particularly like Borland's solution, How can I resize an array?, (sorry Joe) since it requires turning off all range checking:  FAQ 1830D

Also look at Borland's Tech Info Report, Dynamically Allocating Arrays:
TI 1093D

Dr. Dobb's Journal on huge arrays in Turbo Pascal: ftp://garbo.uwasa.fi/pc/turbopas/ddj8803.zip

Dr. Dobb's Journal on virtual arrays in Turbo Pascal: ftp://garbo.uwasa.fi/pc/turbopas/ddj8810.zip

C-8. 2D dynamic arrays D5 Example using new ARRAY OF ARRAY construct introduced in D4:

   How to create a Bitmap from numeric data? (D5)
Shows how to read a TXT file into a TStringList.  Parses each line in TStringList and forms a dynamic matrix of real values to be displayed in a bitmap.

// Delphi 3 Example of 2-Dimensional Dynamic Array
CONST
ArrayMaxCount = 65536;

TYPE
pDoubleArray = ^TDoubleArray;
TDoubleArray = ARRAY[0..ArrayMaxCount-1] OF DOUBLE;
pMatrix = ^TMatrix;
TMatrix = ARRAY[0..ArrayMaxCount-1] OF pDoubleArray;

procedure TForm1.Button1Click(Sender: TObject);
  CONST
    iCountMax = 500; // columns
    jCountMax = 1000; // rows
  VAR
    i : 0..iCountMax-1;
    j : 0..jCountMax-1;
    k : INTEGER;

    // Let's make Matrix 1000 rows of 500 columns =
    // 500,000 elements = 4,000,000 bytes
    Matrix: pMatrix;

    Sum : DOUBLE;
begin
  // Allocate the pointers to each row
  GetMem(Matrix, jCountMax*SizeOf(pMatrix));
  TRY

    // Allocate each row of DOUBLEs
    FOR j := 0 TO jCountMax-1 DO
      GetMem(Matrix[j], iCountMax*SizeOf(DOUBLE));

    TRY
      // Assign value to each Matrix element
      k := 1;
      FOR j := 0 TO jCountMax - 1 DO
      BEGIN
        FOR i := 0 TO iCountMax -1 DO
        BEGIN
          Matrix[j]^[i] := k;
          INC(k);
        END;
      END;

      // Add up all values in Matrix. The sum of 1..N = N*(N+1)/2, so
      // the sum should be (iCountMax*jCountMax)*(iCountMax*jCountMax + 1) / 2,
      // or in this case SUM(1..500,000) = 125,000,250,000.
      Sum := 0;
      FOR j := 0 TO jCountMax - 1 DO
        FOR i := 0 TO iCountMax -1 DO
          Sum := Sum + Matrix[j]^[i];

      Button1.Caption := FloatToStr(Sum);

    FINALLY
      // Free each row of DOUBLEs
      FOR j := jCountMax-1 DOWNTO 0 DO
        FreeMem(Matrix[j]);

    END
  FINALLY
    // Free array of pointers
    FreeMem(Matrix)
  END
end;

C-9. Open Array Parameters;
Slice Function
From the Delphi 3 Object Pascal Language Guide: Open-array parameters allow arrays of varying sizes to be passed to the same procedure or function. A formal parameter declared using the syntax: ARRAY OF T. Within the procedure or function, the formal parameter behaves as if it was declared as ARRAY[0..N - 1] OF T, where N is the number of elements in the actual parameter. Note: When applied to open-array parameters, the Low standard function returns zero, the High standard function returns the index of the last element in the actual array parameter.

The Slice function was introduced in Delphi 2. Use Slice for passing variable-sized "open" arrays to certain functions, e.g., mean, sum, SumInt, MaxValue, MaxIntValue, etc. in Borland math unit, or user-written routines.

Simple Slice Example:

CONST
  PointMaxCount = 25;

VAR
  Points : ARRAY[0..PointMaxCount-1] OF TPoint;
begin
  Points[ 0] := Point( 20, 40);
  Points[ 1] := Point( 30,400);
  Points[ 2] := Point( 40,450);
  Points[ 3] := Point(600,460);
...
  PointCount := 14; // or some other value < PointMaxCount
...
  Bitmap.Canvas.Polyline(Slice(Points, PointCount));

Example defining open array function, dynamic allocation of array, and Slice:

// Calculate median intensity of RGB array
FUNCTION RGBMedian(RGB: ARRAY OF TRGBTriple): TRGBTriple;
  TYPE
    pIntegerArray = ^TIntegerArray;
    TIntegerArray = ARRAY[0..MaxPixelCount-1] OF INTEGER;
  VAR
    Count           : INTEGER;
    i                    : INTEGER;
    MedianIndex: INTEGER;
    MedianValue: INTEGER;
    pIntensity     : pIntegerArray;
BEGIN
  Count := High(RGB) - Low(RGB) + 1;

  // Allocate temporary integer array for intensity values
  GetMem(pIntensity, Count*SizeOf(Integer));

  TRY
    FOR i := Low(RGB) to High(RGB) DO
    BEGIN
      pIntensity^[i] := RGBIntensity(RGB[i]);
    END;

    MedianValue := MedianInteger( Slice(pIntensity^, Count) );

    MedianIndex := Low(RGB);
    WHILE (MedianIndex < High(RGB)) AND (pIntensity^[MedianIndex] <> MedianValue) DO
    BEGIN
      INC (MedianIndex)
    END

  FINALLY
    FreeMem(pIntensity)
  END;

  RESULT := RGB[MedianIndex]
END {RGBMedian};

The above RGBMedian function is called by the following:

RowLast := BitmapIn.Scanline[j-1];

AssignRGBTriple(rowOut[i],
RGBMedian([RowLast[i-1], RowLast[i], RowLast[i+1],
RowIn[i-1], RowIn[i], RowIn[i+1],
RowNext[i-1], RowNext[i+1], RowNext[i+1] ]) );

An Open Array function called by the above example:

FUNCTION MedianInteger (x: ARRAY OF INTEGER): INTEGER;
   VAR
    i        : INTEGER;
    j        : INTEGER;
    Middle   : INTEGER;
    Temporary: INTEGER;
BEGIN
  // Use truncated selection sort to find median

  Middle := (High(x)+1) DIV 2;

  FOR i := 0 TO Middle DO
  BEGIN
    FOR j := 1 TO High(x)-i DO
    BEGIN
      IF x[j] > x[j-1]
      THEN BEGIN
        Temporary := x[j];
        x[j] := x[j-1];
        x[j-1] := Temporary
      END
    END

  END;

  IF Odd(High(x))
  THEN BEGIN
    // When High(x) is Odd, there are an even number of elements in array.
    // Define median as average of two middle values.
    RESULT := (x[middle] + x[middle-1]) DIV 2
  END
  ELSE BEGIN
    // When High(x) is Even, there are an odd number of elements in array.
    // Median is the middle value.
    RESULT := x[middle]
  END
END {MedianInteger};

C-10. Passing Multidimensional Arrays as Parameters Borland's TI 1477D
C-11.  How to use array of const Borland TI 582D
C-12. Dynamic Arrays? A Class Wrapper for TList Delphi Informant article, Feb. 1997, pp. 58-65. 
C-13. Linked Lists -- When the Data is Too Dynamic for Arrays Delphi Informant article, May 1998, pp. 64-67. Source code is
www.informant.com/libs/delphi/3x/DI9805RS.ZIP
C-14. Dynamic array of TPoints to draw a polygon See Borland's FAQ 919D
C-15. Initialization of two-dimensional array CONST
  A: ARRAY[1..2, 1..3] OF INTEGER = ( (1,2,3), (4,5,6));

Also see:   Joselito G. Real UseNet Post about "Initializing n-dimensional array-type constants or static arrays"

C-16. Pointer Math Subject: Re: Pointer Math
From: Earl F. Glynn 
Date: Thursday, February 19, 1998 10:09 PM
Newsgroups: borland.public.delphi.winapi
...
In Delphi 2/3, if p is a pointer, you can always do something like this:

p := Pointer(INTEGER(p) + 8);

If p were a ^INTEGER before the above statement, it will point to "p+8" afterwards.

For Delphi 1, see Borland's
- TI 926D "How to do pointer arithmetic in Delphi"
- TI 1542D, "A better way to do pointer arithmetic"
C-17. Numeric Edit Box The NumEdit component is an edit box that only accepts valid numeric values either from the keyboard or through code. Download from Delphi Super Page: http://delphi.icm.edu.pl/ftp/d10free/numedit.zip
C-18.  Numeric panel Jean-Yves Quéinec's NumPanel demo program.  Numpanel is a program sample (not a component) for "mouse only" numeric matrix input. It's very easy to use and simple to code. Two separate zip files contain the program (Numpanel.zip), and the text (Numpaneldoc.zip) in Html format with 3 images.

Updated
  05 Dec 2005


since
1 Nov 1998