I have the following function in my program and it is giving me an EInvalidOp (Invalid Floating Point Operation):
function TMyProgram.GetVal(A, B, C, D, E: double): double;
begin
Result := A/Power((C - D)/(D - B), 1/E);
end;
The values of the parameters are:
A: 320.068,
B: 84.46,
C: 91.632,
D: 24.15,
E: 11
Excel gives me a result of -316.815, but Delphi is giving me an error when I execute this function.
I did a bit more research. The problem is raising a negative base to a fractional exponent. In your particular case you can use mathematical identity to get around it by doing the following:
function TMyProgram.GetVal(A, B, C, D: Double; E: Integer): double;
begin
if Odd(E) and ((C - D)/(D - B) < 0) then
Result := A/-Power(Abs((C - D)/(D - B)), 1/E)
else
Result := A/Power((C - D)/(D - B), 1/E);
end;
This only works when E is an odd number.
-316.81520613
Thats what i get with the power2 function by Jack Lyle.
look here for full code power2
{** A power function from Jack Lyle. Said to be more powerful than the
Pow function that comes with Delphi. }
function Power2(Base, Exponent : Double) : Double;
{ raises the base to the exponent }
CONST
cTiny = 1e-15;
VAR
Power : Double; { Value before sign correction }
BEGIN
Power := 0;
{ Deal with the near zero special cases }
IF (Abs(Base) < cTiny) THEN BEGIN
Base := 0.0;
END; { IF }
... // see the link to full code
END; { FUNCTION Pow }
Related
I need to implement Seidel method in Pascal. I tried this code but it gives the wrong answer. I don't understand what the mistake is. This is what the procedure for finding roots looks like:
procedure Seidel(n: Integer; var x: vector; a: matrix; e: Real);
var k, i, j, z: integer;
s: Real;
begin
for k := 1 to 100 do
begin
z := k;
for i := 1 to n do
begin
s := a[i, n + 1];
for j := 1 to n do s := s - a[i, j] * x[j];
s := s / a[i, i];
x[i] := x[i] + s;
if abs(s) > e then z := 0
end;
if z <> 0 then Break;
end;
end;
Procedure for variable 'a'
procedure ReadA;
var i, j: integer;
begin
for i := 1 to m do
for j := 1 to m + 1 do
a[i, j] := StrToFloat(Form1.StringGrid1.Cells[j, i])
end;
This is how StringGrid looks like:
"Корни Х" - "Roots X"
When you click on the "Расчёт" (calculate) button, the answers are different, and after repeated clicking, the "Floating point overflow" error appears.
The mistakes are
using no comments
using more than 2 single-letter variable names
using anti-patterns: a counting loop (for loop) should be used only if you can predict the exact number of iterations. Break does/should not belong to your standard repertoire, I even consider it a variant of spaghetti code. There are very few exceptions to this rule, but here you it’s better to stick to using a conditional loop (while … do or repeat … until).
omitting begin … end frames (for branches and loops) during development, when your program evidently is not finished yet
To be fair, the Seidel method can be confusing. On the other hand, Pascal is, provided a sufficient language proficiency, pretty well-suited for such a task.
I actually had to program that task myself in order to possibly understand why your procedure does not produce the right result. The following program uses some Extended Pascal (ISO 10206) features like schemata and type inquiries. You will need an EP-compliant compiler for that, such as the GPC (GNU Pascal Compiler). AFAIK, Delphi does not support those features, but it should be an easy task to resolve any deficiencies.
Considering all aforementioned “mistakes” you arrive at the following solution.
program seidel(output);
type
naturalNumber = 1..maxInt value 1;
All naturalNumber values below are initialized with 1 unless otherwise specified. This is an EP extension.
linearSystem(
coefficientCount: naturalNumber;
equationCount: naturalNumber
) = record
coefficient: array[1..equationCount, 1..coefficientCount] of real;
result: array[1..coefficientCount] of real;
solution: array[1..equationCount] of real;
end;
Of course you may structure that data type differently depending on your main usage scenario.
{
Approximates the solution of the passed linearSystem
using the Gauss-Seidel method.
system.solution should contain an estimate of the/a solution.
}
procedure approximateSolution(var system: linearSystem);
{ Returns `true` if any element along the main diagonal is zero. }
{ NB: There is a chance of false negatives. }
function mainDiagonalNonZero: Boolean;
var
product: real value 1.0;
n: naturalNumber;
begin
{ Take the product of all elements along the main diagonal. }
{ If any element is zero, the entire product is zero. }
for n := 1 to system.coefficientCount do
begin
product := product * system.coefficient[n, n];
end;
mainDiagonalNonZero := product <> 0.0;
end;
This function mainDiagonalNonZero serves as a reminder that you can “nest” routines in routines. Although it is only called once below, it cleans up your source code a bit if you structure units of code like that.
type
{ This is more readable than using plain integer values. }
relativeOrder = (previous, next);
var
approximation: array[relativeOrder] of type of system.solution;
Note, that approximation is declared in front of getNextApproximationResidual, so both this function and the main block of approximateSolution can access the same vectors.
{ Calculates the next approximation vector. }
function getNextApproximationResidual: real;
var
{ used for both, identifying the equation and a coefficient }
n: naturalNumber;
{ used for identifying one term, i.e. coefficient × solution }
term: 0..maxInt;
{ denotes a current error of this new/next approximation }
residual: real;
{ denotes the largest error }
residualMaximum: real value 0.0;
{ for simplicity, you could use `approximation[next, n]` instead }
sum: real;
begin
for n := 1 to system.equationCount do
begin
sum := 0.0;
for term := 1 to n - 1 do
begin
sum := sum + system.coefficient[n, term] * approximation[next, term];
end;
{ term = n is skipped, because that's what we're calculating }
for term := n + 1 to system.equationCount do
begin
sum := sum + system.coefficient[n, term] * approximation[previous, term];
end;
Here it becomes apparent, that your implementation does not contain two for loops. It does not iterate over all terms.
sum := system.result[n] - sum;
{ everything times the reciprocal of coefficient[n, n] }
approximation[next, n] := sum / system.coefficient[n, n];
{ finally, check for larger error }
residual := abs(approximation[next, n] - approximation[previous, n]);
if residual > residualMaximum then
begin
residualMaximum := residual;
end;
end;
getNextApproximationResidual := residualMaximum;
end;
I have outsourced this function getNextApproximationResidual so I could write a nicer abort condition in the loop below.
const
{ Perform at most this many approximations before giving up. }
limit = 1337;
{ If the approximation improved less than this value, }
{ we consider the approximation satisfactory enough. }
errorThreshold = 8 * epsReal;
var
iteration: naturalNumber;
begin
if system.coefficientCount <> system.equationCount then
begin
writeLn('Error: Gauss-Seidel method only works ',
'on a _square_ system of linear equations.');
halt;
end;
{ Values in the main diagonal later appear as divisors, }
{ that means they must be non-zero. }
if not mainDiagonalNonZero then
begin
writeLn('Error: supplied linear system contains ',
'at least one zero along main diagonal.');
halt;
end;
Do not trust user input. Before we calculate anything, ensure the system meets some basic requirements. halt (without any parameters) is an EP extension. Some compilers’ halt also accept an integer parameter to communicate the error condition to the OS.
{ Take system.solution as a first approximation. }
approximation[next] := system.solution;
repeat
begin
iteration := iteration + 1;
{ approximation[next] is overwritten by `getNextApproximationError` }
approximation[previous] := approximation[next];
end
until (getNextApproximationResidual < errorThreshold) or_else (iteration >= limit);
The or_else operator is an EP extension. It explicitly denotes “lazy/short-cut evaluation”. Here it wasn’t necessary, but I like it nevertheless.
{ Emit a warning if the previous loop terminated }
{ because of reaching the maximum number of iterations. }
if iteration >= limit then
begin
writeLn('Note: Maximum number of iterations reached. ',
'Approximation may be significantly off, ',
'or it does not converge.');
end;
{ Finally copy back our best approximation. }
system.solution := approximation[next];
end;
I used the following for testing purposes. protected (EP) corresponds to const in Delphi (I guess).
{ Suitable for printing a small linear system. }
procedure print(protected system: linearSystem);
const
totalWidth = 8;
fractionWidth = 3;
times = ' × ';
plus = ' + ';
var
equation, term: naturalNumber;
begin
for equation := 1 to system.equationCount do
begin
write(system.coefficient[equation, 1]:totalWidth:fractionWidth,
times,
system.solution[1]:totalWidth:fractionWidth);
for term := 2 to system.coefficientCount do
begin
write(plus,
system.coefficient[equation, term]:totalWidth:fractionWidth,
times,
system.solution[term]:totalWidth:fractionWidth);
end;
writeLn('⩰ ':8, system.result[equation]:totalWidth:fractionWidth);
end;
end;
The following example system of linear equations was taken from Wikipedia, so I “knew” the correct result:
{ === MAIN ============================================================= }
var
example: linearSystem(2, 2);
begin
with example do
begin
{ first equation }
coefficient[1, 1] := 16.0;
coefficient[1, 2] := 3.0;
result[1] := 11.0;
{ second equation }
coefficient[2, 1] := 7.0;
coefficient[2, 2] := -11.0;
result[2] := 13.0;
{ used as an estimate }
solution[1] := 1.0;
solution[2] := 1.0;
end;
approximateSolution(example);
print(example);
end.
So I've come up with the code to the values of the triangle itself. What I'm currently strugling is how to aligne/center the values that are printed. I tried many things but, I could use some help now. If anyone has an idea how this can be done feel free to share! Thank you
Program Tri_pas;
Uses Crt;
Var
linha,ordem,a,b: byte;
Function fat(X: byte): real; // factorial
Var fat1: real;
Begin
fat1:=1;
If X <= 1 Then
fat:=1
Else
Begin
Repeat
fat1:=(fat1 * X);
X:=(X - 1);
Until X <= 1;
fat:=fat1;
End;
End;
Procedure nCp(n,p: byte); //Combinations
Var
i: byte;
nCp: real;
Begin
If n < 1 Then
n:=0
Else
n:=(n-1);
For i:=0 to n do
Begin
writeln;
For p:=0 to i do
Begin
nCp:= fat(i) / (fat(p) * fat(i - p)); // mathematic formula for the combinations
Write(nCp:1:0,' ');
End;
End;
End;
{ Main Programa }
Begin
Write('Insert a line(1 -> n) : ');
Readln(linha);
nCp(linha,ordem);
readln;
End.
Just add appropriate number of empty spaces before strings. Note that I used double-spaces, and changed placeholder size to 4 (3+1) to make better formatting.
For p := 1 to (n - i) do
Write(' ');
For p:=0 to i do
Begin
nCp:= fat(i) / (fat(p) * fat(i - p)); // mathematic formula for the combinations
Write(nCp:3:0,' ');
End;
P.S. There are more effective ways to calculate Ncr in reasonable range without real numbers.
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.
Because of a documented rounding issue in Delphi XE2, we are using a special rounding unit available on the Embarcadero site named DecimalRounding_JH1 to achieve true bankers rounding. A link to the unit can be found here:
DecimalRounding_JH1
Using this unit's DecimalRound function with numbers containing a large number of decimal place we
This is the rounding routine from the DecimalRounding_JH1 unit. In our example we call this DecimalRound function with the following parameters (166426800, 12, MaxRelErrDbl, drHalfEven) where maxRelErrDbl = 2.2204460493e-16 * 1.234375 * 2
Function DecimalRound(Value: extended; NDFD: integer; MaxRelErr: double;
Ctrl: tDecimalRoundingCtrl = drHalfEven): extended;
{ The DecimalRounding function is for doing the best possible job of rounding
floating binary point numbers to the specified (NDFD) number of decimal
fraction digits. MaxRelErr is the maximum relative error that will allowed
when determining when to apply the rounding rule. }
var i64, j64: Int64; k: integer; m, ScaledVal, ScaledErr: extended;
begin
If IsNaN(Value) or (Ctrl = drNone)
then begin Result := Value; EXIT end;
Assert(MaxRelErr > 0,
'MaxRelErr param in call to DecimalRound() must be greater than zero.');
{ Compute 10^NDFD and scale the Value and MaxError: }
m := 1; For k := 1 to abs(NDFD) do m := m*10;
If NDFD >= 0
then begin
ScaledVal := Value * m;
ScaledErr := abs(MaxRelErr*Value) * m;
end
else begin
ScaledVal := Value / m;
ScaledErr := abs(MaxRelErr*Value) / m;
end;
{ Do the diferent basic types separately: }
Case Ctrl of
drHalfEven: begin
**i64 := round((ScaledVal - ScaledErr));**
The last line is where we get a floating point error.
Any thoughts on why this error is occurring?
If you get an exception, that means you cannot represent your value as an double within specified error range.
In other words, the maxRelErrDbl is too small.
Try with maxRelErrDbl = 0,0000000001 or something to test if I am right.
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);