Recent

Author Topic: decimal floating point  (Read 1293 times)

srvaldez

  • Full Member
  • ***
  • Posts: 127
decimal floating point
« on: February 17, 2024, 10:20:05 pm »
I am a beginner FreePascal tinkerer, here's a translation of my decimal math routines
note that the str2fp is especially horrible, it tries to parse a numeric string ignoring spaces or any non-number characters so it's incredibly clunky
criticism is welcome
the source code is about 2000 lines so you can download the zipped file from https://u.pcloud.link/publink/show?code=XZICyJ0ZuSSoJh415mSOWzyfB9O6UYzwJ4R7

Bart

  • Hero Member
  • *****
  • Posts: 5563
    • Bart en Mariska's Webstek
Re: decimal floating point
« Reply #1 on: February 17, 2024, 11:35:24 pm »
Not exactly sure what his is about, but I saw multiple goto's, which made me shudder...

Bart

srvaldez

  • Full Member
  • ***
  • Posts: 127
Re: decimal floating point
« Reply #2 on: February 17, 2024, 11:56:27 pm »
parsing a string is always difficult for me, did you run the code?
I tried to use the break statement but it failed to work in that convoluted code
« Last Edit: February 17, 2024, 11:59:40 pm by srvaldez »

TRon

  • Hero Member
  • *****
  • Posts: 4351
Re: decimal floating point
« Reply #3 on: February 18, 2024, 12:04:38 am »
I tried to use the break statement but it failed to work in that convoluted code
Instead of break (which will stop the while loop) you should be using continue.
Today is tomorrow's yesterday.

srvaldez

  • Full Member
  • ***
  • Posts: 127
Re: decimal floating point
« Reply #4 on: February 18, 2024, 12:17:04 am »
thank you TRon for the tip  :)
code updated.
« Last Edit: February 18, 2024, 12:35:04 am by srvaldez »

srvaldez

  • Full Member
  • ***
  • Posts: 127
Re: decimal floating point
« Reply #5 on: February 18, 2024, 03:15:27 am »
provided that you declare fpln2 after fppi at the top, then you can implement the log function as follows
Code: [Select]
function agm(const a_in:decfloat; const b_in:decfloat; const digits:longint=num_digits) : decfloat;
var
a, b, c, d, eps:decfloat;
s:string;
begin
a:=a_in;
b:=b_in;
str(digits-5, s);
eps:='1e-'+s;
repeat
c := (a + b)/2;
d := sqrt(a*b);
a := c;
b := d;
until abs(a-b)<eps;
result:=a;
end;

function calcln2(const digits:longint=num_digits) : decfloat;
begin
result:=decfloat(2);
result:=fpipow(result, -2*digits+1);
result:=(fppi/(2*agm(1, result, digits)))/(2 * digits + 1);
end;

function log_agm(const x:decfloat; const digits:longint=num_digits) : decfloat;
var
y:decfloat;
begin

if x <= 0 then
begin
writeln('can nott take fplog_agm of zero or a negative number');
exit(0);
end;

if x=1 then exit(0);

if x < 1 then
begin
y:=2;
y:=fpipow(y, 2 - 2 * digits);
y*=x;
y:=fppi/(2*agm(1, y, digits))-fpln2 * 2 * digits;
y.sign:=-1;
end
else
begin
y:=2;
y:=fpipow(y, 2 - 2 * digits);
y/=x;
y:=fppi/(2*agm(1, y, digits))-fpln2 * 2 * digits;
end;
result:=y
end;
you would first calculate fpln2 before calling the log_agm function
on my PC taking the log_agm of 2e1000 to 10000 digits (num_dwords=1250) of precision took less that a second
« Last Edit: February 18, 2024, 03:20:10 am by srvaldez »

TRon

  • Hero Member
  • *****
  • Posts: 4351
Re: decimal floating point
« Reply #6 on: February 18, 2024, 08:07:23 am »
thank you TRon for the tip  :)
You're welcome  :)

Quote
code updated.
That was quick  :D  (Yes, I am slow with this response but I noticed the refreshed upload directly after your reaction).
Today is tomorrow's yesterday.

srvaldez

  • Full Member
  • ***
  • Posts: 127
Re: decimal floating point
« Reply #7 on: February 19, 2024, 12:51:21 pm »
I need help, I use the geany IDE because of the multi-language support
something is tripping geany in that it won't show any functions/procedures after fpdiv in the source explorer pane
would you inspect the function fpdiv to see what could trip-up geany's source parser ?   I would be very grateful
the complete source is in the zip file linked in the OP, here's just the fpdiv function
Code: [Select]
function fpdiv(const x:decfloat; const y:decfloat; const dwords_in:longint = num_dwords) : decfloat;
const b = 10000;
var
dwords, i, is_power_of_ten, ubn, ubw:longint;
j, last, laststep, q, t, stp:longint;
xd, xn, rund:double;
resul: array of double;
n: array of double;
d: array of double;
w: array of double;
fac1, fac2:decfloat;
begin
dwords := dwords_in;
if (dwords > num_dwords) then dwords := num_dwords;
ubn := 2 * dwords + 4;
ubw := ubn + 5;
fac1 := x;
fac2 := y;

if (fac2.exponent = 0) then // if fac2 = 0, return
begin
// a divide-by-zero error and
// bail out.
for i := 0 to dwords do
fac1.mantissa[i] := 99999999;

fac1.exponent := 99999 + bias + 1;
//er=DIVZ_ERR;
exit(fac1);
end
else if (fac1.exponent = 0) then //fact1=0, just return
exit(fac1)
else
begin
//check to see if fac2 is a power of ten
is_power_of_ten := 0;
if (fac2.mantissa[0] = 10000000) then
begin
is_power_of_ten := 1;
for i := 1 to dwords do
begin
if (fac2.mantissa[i] <> 0) then
is_power_of_ten := 0;
break;
end;
end;
end;
//if fac2 is a power of ten then all we need to do is to adjust the sign and exponent and we are finished
if (is_power_of_ten = 1) then
begin
fac1.sign := fac1.sign xor fac2.sign;
fac1.exponent := fac1.exponent - fac2.exponent + bias + 1;
exit(fac1);
end;
setLength(w, ubw + 1);
setLength(resul, ubn + 5);
setLength(n, ubn + 5);
setLength(d, ubn + 5);

//double w[ubw];

for j := 0 to dwords do
begin
n[2 * j + 2] := fac1.mantissa[j] div 10000;
n[2 * j + 3] := fac1.mantissa[j] mod 10000;
d[2 * j + 2] := fac2.mantissa[j] div 10000;
d[2 * j + 3] := fac2.mantissa[j] mod 10000;
end;
n[1] := (fac1.exponent and $7fffffff) - bias - 1;
d[1] := (fac2.exponent and $7fffffff) - bias - 1;
for j := ubn to ubw do
w[j] := 0;
t := ubn - 1;
w[1] := n[1] - d[1] + 1;
w[2] := 0;
for j := 2 to ubn do
w[j + 1] := n[j];
xd := (d[2] * b + d[3]) * b + d[4] + d[5] / b;
laststep := t + 2;
for stp := 1 to laststep do
begin
xn := realw(w, (stp + 2), ubw);
q := round(xn / xd);
last := min(stp + t + 1, ubw);
subtract(w, q, d, (stp + 2), last);
normalize(w, (stp + 2), q);
end;
finalnorm(w, (laststep + 1));
If (w[2] <> 0) Then
dec(laststep);
rund := w[laststep + 1] / b;
w[laststep] := w[laststep];
If (rund >= 0.5) Then
w[laststep]+=1;
if (w[2] = 0) then
begin
for j := 1 to (t + 1) do
resul[j] := w[j + 1];
end
else
begin
for j := 1 to (t + 1) do
resul[j] := w[j];
end;
// left-shift out leading 0's if any to preserve accuracy
{if 1}
j := 0;
if (resul[2] < 10) then
j := 3
else if (resul[2] < 100) then
j := 2
else if (resul[2] < 1000) then
j := 1;

if (j > 0) then
LSHIFT_da(resul, 2 * dwords + 4, j);
{endif}
resul[1] := w[1];
If w[2] = 0 Then
resul[1]-=1;

for j := 0 to dwords do
fac1.mantissa[j] := round(resul[2 * j + 2] * 10000 + resul[2 * j + 3]);

j := norm_fac1(fac1, dwords);
fac1.exponent := round(resul[1] + bias);

fac1.sign := fac1.sign xor fac2.sign;
result := fac1;
end;

 

TinyPortal © 2005-2018