Adding Warp Speed to a Conversion family - delphi

I have added a Speed family to the ConvUtils using meters per second as the base.
i.e.
Cf := RegisterConversionFamily('Speed');
RegisterConversionType(Cf,'Meters per second' ,1);
RegisterConversionType(Cf,'Speed of Light' ,0.000000003335641);
RegisterConversionType(Cf,'Speed of Sound(Mach)' ,0.0029411765);
Now I would like to add 'Warp Speed' as a type.
I have the formula
V = wf^3 * C
Where:
V = Velocity
wf = Warp Factor
C = Speed of light
How can I add a formula to add a type?

You can register own formulas for conversion:
function ToWarp(const AValue: Double): Double;
begin
Result := YourFormula1;
end;
function FromWarp(const AValue: Double): Double;
begin
Result := YourFormula2;
end;
RegisterConversionType(Cf,'Warpspeed' ,ToWarp,FromWarp);

Related

How do I split any number into its parts?

I have used this code to split into parts
How to find the numbers in the thousands, hundreds, tens, and ones place in DELPHI for an input number? For example: 155 has 5 ones, 5 tens, etc
however I now need to handle floats.
eg. 101.01
type TSplitNumber = record
Hundreds : integer
Tens : integer
Ones : integer
DecimalPl1 : integer //this will contain 0
DecimalPl2 : integer //this will contain 1
DecimalPl3 : integer
end;
Heres is implementation so far but it only handles non-floats.
type TDivisions = record
Hundreds : integer;
Tens : integer;
Ones : integer;
end;
function SplitNumberIntoDivisions(number : integer) : TDivisions;
var
p : integer;
Ones : integer;
Tens : integer;
Hundreds : integer;
MyDivision : TDivisions;
begin
p := 1;
while number <> 0 do
begin
if p = 1 then
begin
MyDivision.Ones := (number mod 10);
number := number div 10;
Inc(p);
end;
if p = 2 then
begin
MyDivision.Tens := (number mod 10);
number := number div 10;
Inc(p);
end;
if p = 3 then
begin
MyDivision.Hundreds := (number mod 10);
number := number div 10;
Inc(p);
end;
Result := MyDivision;
end;
end;
Anyone got any idea on how to do this?
Ben
First of all, recognise what your float is. Depending on the architecture you will have a certain number of significant digits. Upto 15 is typical but certain architectures may (at some point) give you more, and BCD as implemented in the RTL will give you up to 64.
You then have a 'power' indicating where the decimal point is. Typically you refer to the parts of the float as the mantissa and exponent.
So your answer is going to comprise a set of dgits, each digit being a power of 10, where the powers of 10 are all consecutive and may be all >0 or all <0 or they could straddle 0.
So you will need a structure to hold your powers of 10 which could be something like:
type TDecimalFactor = class(TObject)
Digit: Integer;
Power: Integer;
end;
You can find out what your largest power of 10 is by taking the base 10 log of the number. (So log(100) is 2 and log(1000) is 3 and log(0.1) is -1).
I suggest it would probably be fairly straightforward to 'normalise' your number by dividing it by the highest power - so you have a number which is between 1 and 9.999999999999999 and you know the power it represents. Then work through the number for a many digits as you want (bearing in mind the resolution of the platform) multiplying the float by 10 each time and decrementing your power by 1.
Sample program for you to play with:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Math, System.Generics.Collections;
type
TDecimalFactor = class(TObject)
protected
_nDigit: Integer;
_nPower: Integer;
function _GetValue(): Double;
public
constructor Create(nDigit, nPower: Integer);
procedure ShowFactor();
property Digit: Integer read _nDigit;
property Power: Integer read _nPower;
property Value: Double read _GetValue;
end;
TDecimalFactors = class(TObjectList<TDecimalFactor>)
protected
function _GetValue(): Double;
public
property Value: Double read _GetValue;
end;
constructor TDecimalFactor.Create(nDigit, nPower: Integer);
begin
Self._nDigit:=nDigit;
Self._nPower:=nPower;
inherited Create();
end;
function TDecimalFactor._GetValue(): Double;
begin
Result:=Self._nDigit*System.Math.Power(10, Self._nPower);
end;
procedure TDecimalFactor.ShowFactor();
begin
writeln('Factor: ', IntToStr(Self._nDigit), ' x ', FloatToStr(System.Math.Power( 10, Self._nPower)));
end;
function TDecimalFactors._GetValue(): Double;
var
pFactor: TDecimalFactor;
begin
Result:=0;
for pFactor in Self do
Result:=Result+pFactor.Value;
end;
var
fValue: Double;
fLog: Double;
nPower: Integer;
fPower: Double;
nDigits: Integer;
nLoop: Integer;
pDigits: TDecimalFactors;
pFactor: TDecimalFactor;
begin
try
pDigits:=TDecimalFactors.Create(true);
fValue:=6.5788902E-5; // try different values here to test operation
writeln('Input Value: '+FloatToStr(fValue));
nDigits:=15;
fLog:=log10(fValue);
nPower:=floor(fLog);
fPower:=Power(10,nPower);
fValue:=fValue/fPower;
nLoop:=0;
while(nLoop<nDigits) do
begin
pFactor:=TDecimalFactor.Create(floor(fValue), nPower);
pDigits.Add(pFactor);
pFactor.ShowFactor();
fValue:=(fValue-pFactor.Digit)*10;
inc(nLoop);
dec(nPower);
// stop the loop when we have got far enough, recognising limited precision
if(SameValue(fValue, 0, 0.00000001)) then
break;
end;
writeln('Factorised Value: '+FloatToStr(pDigits.Value));
FreeAndNil(pDigits);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.

Easiest way to find the mean of a dynamic array

I have created a dynamic array, and have passed values to it. Is there a shortcut for finding mean of dynamic array.
var
TheMin, TheMax: Integer;
x: array of Integer; //Dynamic array declaration
....
TheMin := MinIntValue(x);//I am able to retrieve the minium value of the dynamic array
TheMax := MaxIntValue(x);//I am able to retrieve the maximum value of the dynamic array
Is there a other way to get mean using Math library.
It is very easy to write such a function.
function Mean(const Data: array of Integer): Double; overload;
var
i: Integer;
begin
Result := 0.0;
for i := low(Data) to high(Data) do
Result := Result + Data[i];
Result := Result / Length(Data);
end;
I overloaded this so that it could sit alongside the same named functions in the Math unit.
If you wish to use built in library code you can use SumInt from the Math unit:
TheMean := SumInt(x) / Length(x);
SumInt performs the summation using an Integer accumulator. This is probably faster than the bespoke function that uses a floating point accumulator. However, an Integer accumulator is potentially subject to overflow which may be off-putting. On the other hand, an Integer accumulator is potentially more accurate than a floating point accumulator. Depending on your usage requirements these issues may be important to you.
In bother cases, if the input array is of length zero a runtime floating point divide by zero error will be raised.
If the array has additions or deletions, recalculating the average from scratch can get rather time consuming.
In that case it may be worthwhile to calculate a running average instead.
function RecalcAverage(OldAverage: double; const OldArray, Additions, Deletions: TIntArray): double; overload;
var
i: integer;
begin
i:= Length(OldArray) + Length(Additions) - Length(Deletions);
WeighingFactor := 1 / i;
Result:= OldAverage;
for i:= 0 to Length(Deletions) -1 do begin
Result:= Result - (Deletions[i] * WeighingFactor);
end;
for i:= 0 to Length(Additions) -1 do begin
Result:= Result + (Additions[i] * WeighingFactor);
end;
end;
If you have a running sum handy, you can avoid the rounding errors and calculate an exact average.
function RecalcAverage(var RunningTotal: Int64; const OldArray, Additions, Deletions: TIntArray): double; overload;
var
i: integer;
begin
for i:= 0 to Length(Deletions) -1 do begin
RunningTotal:= RunningTotal - Deletions[i];
end;
for i:= 0 to Length(Additions) -1 do begin
RunningTotal:= RunningTotal + Additions[i];
end;
Result:= RunningTotal / (Length(OldArray) + Length(Additions) - Length(Deletions));
end;
If performance is an issue, it would make much more sense to calculate all the needed values in a single loop.
type
TStats = record
MaxVal: integer;
MinVal: integer;
Average: double;
end;
function CalcStats(const input: TIntArray): TStats;
var
MinVal, MaxVal: integer;
Total: Int64;
i: integer;
begin
Assert(Length(input) > 0);
MinVal:= input[0];
MaxVal:= MinVal;
Total:= MinVal;
for i:= 1 to Length(input) -1 do begin
MinVal:= Min(MinVal, input[i]);
MaxVal:= Max(MinVal, input[i]);
Total:= Total + input[i];
end;
Result.MinVal:= MinVal;
Result.MaxVal:= MaxVal;
Result.Average:= Total / Length(input);
end;

How do I use the modulo function with this library?

I am Using this library for Big Integers in Pascal but I am having trouble using the modulo function. Can anyone help?
My code:
a = b modulo(c);
here is the library location: http://www.delphiforfun.org/programs/library/big_integers.htm
{ ***************** Modulo ************* }
procedure TInteger.Modulo(const I2: TInteger);
{ Modulo (remainder after division) - by TInteger }
var
k: int64;
imod3: TInteger;
begin
if high(I2.fDigits) = 0 then begin
divmodsmall(I2.Sign * I2.fDigits[0], k);
assignsmall(k);
end
else
begin
imod3:= GetNextScratchPad;
DivideRem(I2, imod3);
Assign(imod3);
ReleaseScratchPad(imod3);
end;
end;
Why does this not work?:
also why doesnt this work?:
var
P, Q, N, E, D,i: TInteger;
Eing, Cout: TInteger;
begin
E := 3;
D := 27;
N := 55;
writeln(N.Modulo(E));
The source code that you downloaded comes with an example of how to use the modulo function. I urge you to take time to read the example code that comes with a library. If you would do so then you'd be able to solve far more problems by yourself. The example code looks like this:
procedure Tbigints.ModBtnClick(Sender: TObject);
var
i1,i2,i3:Tinteger;
begin
i1:=TInteger.create(0);
i2:=TInteger.create(0);
Getxy(i1,i2);
i1.modulo(i2);
memo1.text:=i1.converttoDecimalString(true);
i1.free;
i2.free;
alloclbl.caption:=format('Allocated memory: %d',[allocmemsize]);
end;
The key point is that the modulo method acts in place. In the code above, the dividend is held in i1 and the divisor in i2. Then you call modulo on i1 passing i2 as the argument. The result of the operation is then placed in i1. So, this method replaces the dividend with the modulus of the division.

Delphi: How do I translate this C code, which performs low-level access to IEEE floating-point number?

The following C function is from fastapprox project.
static inline float
fasterlog2 (float x)
{
union { float f; uint32_t i; } vx = { x };
float y = vx.i;
y *= 1.1920928955078125e-7f;
return y - 126.94269504f;
}
I know that C union can be translated to Delphi variant record, but I still experienced difficulty in translating such low-level C code to Delphi. I hope Delphi experts here are willing to help.
More Information
I add this section later, which is not a part of the question. This section gives information to readers that expect better accuracy.
In fastapprox, fasterlog2() was deliberately designed to be simpler, faster but less accurate Log2 function. Anyone who expect better accuracy can use the more-accurate function they provide, namely fastlog2().
They included a Mathematica notebook with an explanation of their algorithms as well as the origin of some mysterious values, e.g.126.94269504. Mathematica website provides a free viewer for the .nb files.
See also: Why the IEEE-754 exponent bias used in this C code is 126.94269504 instead of 127?
I think I'd code it by using pointer casts to effect a reinterpret cast:
function fasterlog2(x: single): single;
const
c1: Single = 1.1920928955078125e-7;
c2: Single = 126.94269504;
var
y: single;
begin
y := PCardinal(#x)^;
Result := y * c1 - c2;
end;
Note that I used typed constants of type single to ensure an exact match with the C code.
I don't really see any need for a variant record in a Delphi implementation.
Or you could use a pure asm approach. The x86 version looks like this:
function fasterlog2asm(x: single): single;
const
c1: Single = 1.1920928955078125e-7;
c2: Single = 126.94269504;
asm
FILD DWORD PTR [ESP+$08]
FMUL c1
FSUB c2
FWAIT
end;
For x64 the SSE implementation would be
function fasterlog2asm64(x: single): single;
const
c1: double = 1.1920928955078125e-7;
c2: double = 126.94269504;
asm
CVTDQ2PD xmm0, xmm0
MULSD xmm0, c1
SUBSD xmm0, c2
CVTSD2SS xmm0, xmm0
end;
In x64 the assembly version is only about twice as performant as the pure pascal function. The x86 assembly version is over five times as performant - this is entirely due to the higher cost of type conversion (integer/single/double) in SSE vs x87.
The reason that this approach can be used is that floating point numbers are represent as
significand * base^exponent
and a value of 2 is used as the base.
How about this:
function fasterlog2(x: Single): Single; inline;
const
f1: Single = 1.1920928955078125e-7;
f2: Single = -126.94269504;
var
i: Cardinal absolute x;
begin
Result := i * f1 + f2;
end;
Try this (literal translation):
function fasterlog2(x : Single): Single; inline;
type
TX = record
case boolean of
false: (f : Single);
true: (i : Cardinal);
end;
const
f1 : Single = 1.1920928955078125e-7;
f2 : Single = -126.94269504;
var
vx: TX absolute x;
y: Single;
begin
y := vx.i;
y := y * f1;
Result := y + f2;
end;
WriteLn(fasterlog2( 1024.0));
WriteLn(Math.Log2( 1024.0));
Outputs:
1.00573043823242E+0001
1.00000000000000E+0001
Or a oneliner (similar to Davids example):
function fasterlog2(x : Single): Single; inline;
const
f1 : Single = 1.1920928955078125e-7;
f2 : Single = -126.94269504;
begin
Result := PCardinal(#x)^ * f1 + f2;
end;
A possible translation is:
function fasterlog2(x: Single): Single;
type
TVx = record
case Byte of
0: (f: Single);
1: (i: UInt32); // Or Cardinal, depending on version
end;
const
C1: Single = 1.1920928955078125e-7;
C2: Single = 126.94269504;
var
y: Single;
vx: TVx;
begin
vx.f := x;
y := vx.i;
y := y * C1;
Result := y - C2;
end;
I assume this somehow uses knowledge of the bit patterns of the Single. I am not sure if it really gives you a faster log, but that is what the C routine is doing.

Problems returning self as a function result

I have a very simple class definition for 3D Vectors, TVector3D, and a few methods used to implement the TVector3D.Normalise function. If I pass the Normalise function a vector that is already normalised, I want it to return the vector I passed it. Here I have used Result := Self but I am having some crazy returns.
The console application:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TVector3D = Class
public
x : Single;
y : Single;
z : Single;
constructor Create(x : Single;
y : Single;
z : Single);
function GetMagnitude() : Single;
function IsUnitVector() : Boolean;
function Normalise() : TVector3D;
end;
constructor TVector3D.Create(x : Single;
y : Single;
z : Single);
begin
Self.x := x;
Self.y := y;
Self.z := z;
end;
function TVector3D.GetMagnitude;
begin
Result := Sqrt(Sqr(Self.x) + Sqr(Self.y) + Sqr(Self.z));
end;
function TVector3D.IsUnitVector;
begin
if Self.GetMagnitude = 1 then
Result := True
else
Result := False;
end;
function TVector3D.Normalise;
var
x : Single;
y : Single;
z : Single;
MagnitudeFactor : Single;
begin
if IsUnitVector then
Result := Self
else
MagnitudeFactor := 1/(Self.GetMagnitude);
x := Self.x*MagnitudeFactor;
y := Self.y*MagnitudeFactor;
z := Self.z*MagnitudeFactor;
Result := TVector3D.Create(x,
y,
z);
end;
procedure TestNormalise;
var
nonUnitVector : TVector3D;
unitVector : TVector3D;
nUVNormed : TVector3D;
uVNormed : TVector3D;
begin
//Setup Vectors for Test
nonUnitVector := TVector3D.Create(1,
1,
1);
unitVector := TVector3D.Create(1,
0,
0);
//Normalise Vectors & Free Memory
nUVNormed := nonUnitVector.Normalise;
nonUnitVector.Free;
uVNormed := unitVector.Normalise;
unitVector.Free;
//Print Output & Free Memory
WriteLn('nUVNormed = (' + FloatToStr(nUVNormed.x) + ', ' + FloatToStr(nUVNormed.y) + ', ' + FloatToStr(nUVNormed.z) + ')');
nUVNormed.Free;
WriteLn('uVNormed = (' + FloatToStr(uVNormed.x) + ', ' + FloatToStr(uVNormed.y) + ', ' + FloatToStr(uVNormed.z) + ')');
uVNormed.Free;
end;
begin
try
TestNormalise;
Sleep(10000);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Normalise works fine for non-unit vecors, i.e. IsUnitVector returns false. But for unit vectors, such as (1,0,0), instead of returning itself I get a result with very low nonzero numbers wherever there was a nonzero previously, such as (8.47122...E-38,0,0).
If I run this through the debugger with a breakpoint on the line Result := Self set to evaluate Self, Self is (1,0,0) yet result becomes (Very Low Number,0,0). Where Very Low Number changes each time I run the programme but always seems to be around E-38/E-39.
I do not understand why this happens. Why does it happen and how is it best to alter my Normalise function to avoid it.
Your current TVector3D.Normalise implementation has some issues:
The last 4 lines are always executed, because you have not used a begin-end block after the else,
So the routine never returns Self, but always a new instance,
The returned instance's memory is propably leaked because you lost ownership of it after the function call,
When IsUnitVector returns True, then the assignment of MagnitudeFactor will be skipped, and it will be a random value (currently present at that memory's address), which explains why you get rubish. You are also warned by the compiler for this: Variable MagnitudeFactor might not have been initialized.
Instead, I would rewrite the routine as follows:
function TVector3D.Normalise: TVector3D;
begin
if not IsUnitVector then
begin
x := x / GetMagnitude;
y := y / GetMagnitude;
z := z / GetMagnitude;
end;
Result := Self;
end;
The root of all your problems is that you are using a class, which is a reference type. Instead you need to make your vector be a value type. That means use a record.
In your code, even when you fix the problem identified by #NGLN, you have still destroyed all instances of your class by the time you start calling WriteLn.
Unless you grasp this issue soon, I fear that you will continue having problems. Switching to using a value type will make your coding trivially easy in comparison to your current approach.
Here's something to get you started:
type
TVector3 = record
public
class operator Negative(const V: TVector3): TVector3;
class operator Equal(const V1, V2: TVector3): Boolean;
class operator NotEqual(const V1, V2: TVector3): Boolean;
class operator Add(const V1, V2: TVector3): TVector3;
class operator Subtract(const V1, V2: TVector3): TVector3;
class operator Multiply(const V: TVector3; const D: Double): TVector3;
class operator Multiply(const D: Double; const V: TVector3): TVector3;
class operator Divide(const V: TVector3; const D: Double): TVector3;
class function New(const X, Y, Z: Double): TVector3; static;
function IsZero: Boolean;
function IsNonZero: Boolean;
function IsUnit: Boolean;
function Mag: Double;
function SqrMag: Double;
function Normalised: TVector3;
function ToString: string;
public
X, Y, Z: Double;
end;
const
ZeroVector3: TVector3=();
class operator TVector3.Negative(const V: TVector3): TVector3;
begin
Result.X := -V.X;
Result.Y := -V.Y;
Result.Z := -V.Z;
end;
class operator TVector3.Equal(const V1, V2: TVector3): Boolean;
begin
Result := (V1.X=V2.X) and (V1.Y=V2.Y) and (V1.Z=V2.Z);
end;
class operator TVector3.NotEqual(const V1, V2: TVector3): Boolean;
begin
Result := not (V1=V2);
end;
class operator TVector3.Add(const V1, V2: TVector3): TVector3;
begin
Result.X := V1.X + V2.X;
Result.Y := V1.Y + V2.Y;
Result.Z := V1.Z + V2.Z;
end;
class operator TVector3.Subtract(const V1, V2: TVector3): TVector3;
begin
Result.X := V1.X - V2.X;
Result.Y := V1.Y - V2.Y;
Result.Z := V1.Z - V2.Z;
end;
class operator TVector3.Multiply(const V: TVector3; const D: Double): TVector3;
begin
Result.X := D*V.X;
Result.Y := D*V.Y;
Result.Z := D*V.Z;
end;
class operator TVector3.Multiply(const D: Double; const V: TVector3): TVector3;
begin
Result.X := D*V.X;
Result.Y := D*V.Y;
Result.Z := D*V.Z;
end;
class operator TVector3.Divide(const V: TVector3; const D: Double): TVector3;
begin
Result := (1.0/D)*V;
end;
class function TVector3.New(const X, Y, Z: Double): TVector3;
begin
Result.X := X;
Result.Y := Y;
Result.Z := Z;
end;
function TVector3.IsZero: Boolean;
begin
Result := Self=ZeroVector3;
end;
function TVector3.IsNonZero: Boolean;
begin
Result := Self<>ZeroVector3;
end;
function TVector3.IsUnit: Boolean;
begin
Result := abs(1.0-Mag)<1.0e-5;
end;
function TVector3.Mag: Double;
begin
Result := Sqrt(X*X + Y*Y + Z*Z);
end;
function TVector3.SqrMag: Double;
begin
Result := X*X + Y*Y + Z*Z;
end;
function TVector3.Normalised;
begin
Result := Self/Mag;
end;
function TVector3.ToString: string;
begin
Result := Format('(%g, %g, %g)', [X, Y, Z]);
end;
This is extracted from my own codebase. I'm using Double, but if you really prefer to use Single, then you can readily change it.
The use of operator overloading makes the code you write so much more readable. Now you can write V3 := V1 + V2 and so on.
Here's what your test code looks like with this record:
var
nonUnitVector: TVector3;
unitVector: TVector3;
nUVNormed: TVector3;
uVNormed: TVector3;
begin
//Setup Vectors for Test
nonUnitVector := TVector3.New(1, 1, 1);
unitVector := TVector3.New(1, 0, 0);
//Normalise Vectors
nUVNormed := nonUnitVector.Normalised;
uVNormed := unitVector.Normalised;
//Print Output
WriteLn('nUVNormed = ' + nUVNormed.ToString);
WriteLn('uVNormed = ' + uVNormed.ToString);
Readln;
end.
Or if you want to compress it somewhat:
WriteLn('nUVNormed = ' + TVector3.New(1, 1, 1).Normalised.ToString);
WriteLn('uVNormed = ' + TVector3.New(1, 0, 0).Normalised.ToString);
A few hints:
First, I'd actually make the vector a record instead of a class if I were you, but YMMV. That would simplify a lot, since the compiler will manage the lifetime of every vector (you never need to worry about freeing things). Second,
function TVector3D.IsUnitVector;
begin
if self.GetMagnitude = 1 then
result := True
else
result := False;
end;
is normally written, syntactically and exactly equivalently,
function TVector3D.IsUnitVector;
begin
result := GetMagnitude = 1
end;
But even so, it is incorrect. Since you are dealing with floating-point numbers, you cannot reliably test equality. Instead, you should see if the magnitude is within some interval of unity, so that 'fuzz' do not interfere. For instance, you could do (uses Math)
function TVector3D.IsUnitVector;
begin
result := IsZero(GetMagnitude - 1)
end;
Third, your Normalize function returns a new vector object if it needs to normalize, and returns the same object if not. That's very confusing. You'd never know how many instances you have! Instead, make this a procedure:
procedure TVector3D.Normalize;
var
norm: single;
begin
norm := GetMagnitude;
x := x / norm;
y := y / norm;
z := z / norm;
end;
Fourth, why use single instead of double or real?
Fifth, as NGLN pointed out (please upvote his answer!), you forgot the begin...end block in the else part of your Normalize function, so the four last lines are always executed! Hence, you always create a new vector instance! Still, my point is very important: your original function 'intends' (if you just add the begin...end block) to return self or create a new instance depending on a condition, which is rather terrible, since then you do not know how many instances you have! (And so, you'll probably begin to leak vectors...)

Resources