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.