Fix64 für Delphi

  • 1
  • 2
  • Page 2 of 2
ThomasW69
 
Avatar
 
Subject:

Re: Fix64 für Delphi

 · 
Posted: 21.12.2010 - 10:38  ·  #9
Hat jemand eine ahnung wie ich sowas in Delphi deklariere?
Code
 TFix64Overlay = record // this is handy for fast extraction of integer and fractional parts
                    fix        : fix64;
                    x  [@fix]  : fix64;
                    i64[@fix]  : int64;
                    w64[@fix]  : word64;
                    i  [@fix+4]: longint;
                    i32[@fix+4]: longint;
                    f  [@fix]  : longword;
                    f32[@fix]  : longword;
                    b  [@fix]  : array[0..7] of byte;
                    w  [@fix]  : array[0..3] of word;
                  end; 


Wenn ich das richtig verstehe wird da mit variablen-ovelay gearbeitet. Ich stehe da gerade mächtig auf dem Schlauch, weil ich in Delphi noch nie mit sowas gearbeitet habe. Habs halt nie gebraucht.
Merlin
Administrator
Avatar
Gender:
Age: 24
Posts: 1373
Registered: 03 / 2005
Subject:

Re: Fix64 für Delphi

 · 
Posted: 21.12.2010 - 12:29  ·  #10
Hi Thomas.

Here is an example.

Code
type tFix64Variants = (Fix64, FixInt64)

tFix64Overlay = record
  case tFix64Variants of
    Fix64: (f, i : int32 );
    FixInt64: (i64 : int64 );
end;


Regards

Merlin
chefe
Benutzer
Avatar
Gender: n/a
Age: 49
Posts: 5
Registered: 07 / 2010
Subject:

Re: Fix64 für Delphi

 · 
Posted: 21.12.2010 - 17:36  ·  #11
Hallo,

packed record nicht vergessen...

chefe
Avra
Schreiberling
Avatar
Gender:
Location: Belgrade, Serbia
Age: 53
Homepage: rs.linkedin.com/in…
Posts: 653
Registered: 07 / 2002
Subject:

Re: Fix64 für Delphi

 · 
Posted: 30.12.2010 - 23:37  ·  #12
Without working Delphi and AvrCo yet, I could just make this with Lazarus. It should work with Delphi without changes. Double was not enough, so extended type was used.

Here is the conversion unit:
Code
unit fix64conv;

{$mode delphi}{$H+}

interface

uses
  Classes, SysUtils, StrUtils;

const
  FIX_INT_DIGITS = 10 + 1; // number of digits used for representation of integer part plus 1 digit for sign
  FIX_FRACT_DIGITS =  9;   // number of digits used for representation of fractional part
  FIX_MAX_STRING_LENGTH = FIX_INT_DIGITS + 1 {decimal point} + FIX_FRACT_DIGITS; // 21 is max length of all strings that contain fixed point numbers
  FIX_ONE = $100000000; // 1.0 is neutral in multiplication and division of fixed point numbers
  FIX_FRACT_MULTIPLIER: int64    = 1000000000; // fractional multiplier needed to get readable numbers from fractional part
  FIX_MASK_FOR_FRACT_PART: int64 = $00000000FFFFFFFF; // needed to mask just bits of fractional part of fixed point numbers

type
  fix64 = int64;
  fix = fix64;
  TFix64 = fix64;
  TFix = fix64;
  word64 = qword;
  TFix64Overlay = packed record case integer of
    // for fast extraction of integer and fractional parts
    0: (fix: fix64);
    1: (x: fix64);
    2: (i64: int64);
    3: (w64: word64);
    4: (f: longword; i: longint);
    5: (f32: longword; i32: longint);
    6: (b: array[0..7] of byte);
    7: (w: array[0..3] of word);
  end;
  TFixString = string[FIX_MAX_STRING_LENGTH]; // strings containing fixed point numbers

function ExtendedToFix64(const a: extended): fix64;
function Fix64ToExtended(const a: fix64): extended;
function Fix64ToString(const a: fix64): TFixString;
function Fix64GetIntPartAsString(const a: fix64): TFixString;
function Fix64GetFractPartAsString(const a: fix64): TFixString;
function Fix64GetIntPart(const a: fix64): longint;
function Fix64GetFractPart(const a: fix64): longword;
function Fix64ToLongInt(const a: fix64): longint;

implementation

function ExtendedToFix64(const a: extended): fix64;
begin
  result := Round(a * FIX_ONE);
end;

function Fix64ToExtended(const a: fix64): extended;
begin
  result := a;
  result := result / FIX_ONE;
end;

function Fix64ToString(const a: fix64): TFixString;
var
  TmpStr: TFixString;
begin
  TmpStr := fix64GetIntPartAsString(a);
  if (int64(a) < 0) and (TmpStr[1] <> '-') then // special case negative numbers between 0 and -1
    Result := '-'
  else
    Result := '';
  Result := Result + TmpStr + DecimalSeparator;
  Result := Result + fix64GetFractPartAsString(a);
end;

function Fix64GetIntPartAsString(const a: fix64): TFixString;
begin
  Result := IntToStr(fix64GetIntPart(a));
end;

function Fix64GetFractPartAsString(const a: fix64): TFixString;
begin
  Result := AddChar('0', IntToStr(fix64GetFractPart(a)), FIX_FRACT_DIGITS); // AddChar is actually PadLeft
end;

function Fix64GetIntPart(const a: fix64): longint;
begin
  Result := Fix64ToLongInt(a);
end;

function Fix64GetFractPart(const a: fix64): longword;
var
  Tmp: TFix64Overlay;
begin
  Tmp.fix := a;
  if Tmp.fix < 0 then
    Tmp.fix := (not Tmp.fix) + 1;
  Tmp.fix := Tmp.fix and FIX_MASK_FOR_FRACT_PART;
  Result := longword((word64(Tmp.f) * word64(FIX_FRACT_MULTIPLIER)) div word64(FIX_ONE));
end;

function Fix64ToLongInt(const a: fix64): longint;
var
  Tmp: TFix64Overlay;
begin
  Tmp.fix := a;
  if (Tmp.i64 < 0) and (Tmp.f <> 0) then // (Tmp.f <> 0) is a correction for round negative numbers
    Tmp.fix := Tmp.fix + FIX_ONE;
  Result := Tmp.i;
end;
end.


To use the demo example below, make new application with single form and put Memo1 and Button1, and replace default code with the one here:
Code
unit demo;

{$mode delphi}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, fix64conv;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end; 

var
  Form1: TForm1; 

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  ext: extended;
  fix: fix64;
  fixovr: TFix64Overlay;
begin
  ext := 3.14;
  fix := ExtendedToFix64(ext);
  Memo1.Clear;
  Memo1.Append(FloatToStr(ext));
  Memo1.Append('$' + IntToHex(fix, 16) + '  ' + IntToStr(fix));
  Memo1.Append('$' + IntToHex(TFix64Overlay(fix).i, 1) + ',' + IntToHex(TFix64Overlay(fix).f, 8));
  ext := Fix64ToExtended(fix);
  Memo1.Append(FloatToStr(ext));
  Memo1.Append(FloatToStr(2147483647.333333333) + '   FloatToStr()'); // FloatToStr handles just double (only 15 significant digits)
  Memo1.Append(FloatToStr(Fix64ToExtended($7FFFFFFFFFFAAAAA)) + '   FloatToStr()'); // FloatToStr does not handle full extended type
  Memo1.Append(Fix64ToString($7FFFFFFFFFFAAAAA) + '   Fix64ToString()'); // my string conversion can handle full extended type
  Memo1.Append(FloatToStr(Fix64ToExtended($7FFFFFFFFFFFFFFF)) + '   FloatToStr()'); //  2147483647.999999999 biggest fix64 number
  Memo1.Append(Fix64ToString($7FFFFFFFFFFFFFFF) + '   Fix64ToString()'); // my string conversion can handle full extended type
  Memo1.Append(FloatToStr(Fix64ToExtended($8000000000000000)) + '   FloatToStr()'); // -2147483648.000000000 smallest fix64 number
  Memo1.Append(Fix64ToString($8000000000000000) + '   Fix64ToString()'); // my string conversion can handle full extended type
  fixovr.fix := fix;      // fixovr.fix = 3,14
  inc(fixovr.i);          // fixovr.fix = 4,14   (increment only integer part by one)
  fixovr.f := $80000000;  // fixovr.fix = 4,50   (replace fractional part with 0,5)
  Memo1.Append(FloatToStr(Fix64ToExtended(fixovr.fix)) + '   FloatToStr()');
  Memo1.Append(Fix64ToString(fixovr.fix) + '   Fix64ToString()');
end;
end.


This should give you a memo saying this:
Code
3,14
$0000000323D70A3D  13486197309
$3,23D70A3D
3,13999999989755
2147483647,33333   FloatToStr()
2147483647,99992   FloatToStr()
2147483647,999918619   Fix64ToString()
2147483648   FloatToStr()
2147483647,999999999   Fix64ToString()
-2147483648   FloatToStr()
-2147483648,000000000   Fix64ToString()
4,5   FloatToStr()
4,500000000   Fix64ToString()

For curious, you can replace 3.14 with 1.0 or 0.5 or -1.0, and try to guess what would it look like in hexadecimal representation of converted fix64 number. I have also noticed that Lazarus FloatToStr() deals just with double, and although it accepts extended type it can not handle more then 15 significant digits (this is limitation of double). This means that if you have 19 significant digits (as in extended type) then some data is lost. I do not know yet if this unwanted feature is also valid for Delphi. So, FloatToStr(Fix64ToExtended($7FFFFFFFFFFAAAAA) gives you just 2147483647,99992 (less significant digits are lost because of just 15 significant digits as limitation of double type), and it is even more evident with 2147483647.999999999 (biggest fix64 number) where FloatToStr(Fix64ToExtended($7FFFFFFFFFFFFFFF)) is rounded and gives just 2147483648. If someone wants to know, smallest fix64 number is $8000000000000000 (-2147483648.000000000). Even if FloatToStr was perfect, there are some fix64 numbers that can not be represented 100% accurate even in extended type. Fix64 numbers have 10 digits for integer part, and 9 digits for fractional part. That matches 19 significant digits of extended type (double is a looser with just 15), but there is one hidden fractional digit never displayed. I do not remember if there were 2 or 3 additional bits that are used in all internal calculations, but they never show when fix64 number is converted to string (since we do not have enough bits to show all 0..9 numbers this last fractional digit was hidden). Therefore, we can say that fix64 has 19+1=20 digits so even extended type is small for our "mighty" fix64 numbers. Joking put a side, I do not see that this will ever affect you calculations.

Report test results here.

EDIT: I have replaced simple TFix64Overlay from the first version with more advanced variant record, which is much closer to AvrCo implementation, and I have also expanded original demo a little. I have also added my Fix64ToString() since it is more accurate then built in functions. Please test it in Delphi.
  • 1
  • 2
  • Page 2 of 2
Selected quotes for multi-quoting:   0

Registered users in this topic

Currently no registered users in this section

The statistic shows who was online during the last 5 minutes. Updated every 90 seconds.
MySQL Queries: 15 · Cache Hits: 14   75   89 · Page-Gen-Time: 0.020761s · Memory Usage: 2 MB · GZIP: on · Viewport: SMXL-HiDPI