RE: Comparison QB64 compiled with Ofast and without - Coolman - 06-10-2022
i hate the c c++ language but i must admit that it is very powerful. it's a pity that qb64 doesn't generate an optimized code like this. i found this code here :
https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/nbody-gpp-3.html
compile :
g++ -c -pipe -O3 -fomit-frame-pointer -std=c++17 n-body.cpp -o n-body.o && g++ n-body.o -o n-body
test :
time ./n-body 50000000
results :
-0.169075164
-0.169059907
real 0m3,352s
user 0m3,348s
n-body.cpp
Code: (Select All) /* The Computer Language Benchmarks Game
https://salsa.debian.org/benchmarksgame-team/benchmarksgame/
contributed by Martin Jambrek
based off the Java #2 program contributed by Mark C. Lewis and modified slightly by Chad Whipkey
*/
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <utility>
static constexpr double PI = 3.141592653589793;
static constexpr double SOLAR_MASS = 4 * PI * PI;
static constexpr double DAYS_PER_YEAR = 365.24;
template <auto start, auto stop, auto step = 1, class F>
constexpr void static_for(F&& f)
{
if constexpr (start < stop) {
f(std::integral_constant<decltype(start), start>());
static_for<start + step, stop, step>(std::move(f));
}
}
struct alignas(32) Body {
double x, y, z, pad0;
double vx, vy, vz, pad1;
double mass;
constexpr Body(double x, double y, double z,
double vx, double vy, double vz,
double mass)
: x(x)
, y(y)
, z(z)
, pad0()
, vx(vx)
, vy(vy)
, vz(vz)
, pad1()
, mass(mass)
{
}
};
static constexpr size_t N_BODIES = 5;
using System = Body[N_BODIES];
static constexpr Body sun = {
0, 0, 0, 0, 0, 0, SOLAR_MASS
};
static constexpr Body jupiter = {
4.84143144246472090e+00,
-1.16032004402742839e+00,
-1.03622044471123109e-01,
1.66007664274403694e-03 * DAYS_PER_YEAR,
7.69901118419740425e-03 * DAYS_PER_YEAR,
-6.90460016972063023e-05 * DAYS_PER_YEAR,
9.54791938424326609e-04 * SOLAR_MASS
};
static constexpr Body saturn = {
8.34336671824457987e+00,
4.12479856412430479e+00,
-4.03523417114321381e-01,
-2.76742510726862411e-03 * DAYS_PER_YEAR,
4.99852801234917238e-03 * DAYS_PER_YEAR,
2.30417297573763929e-05 * DAYS_PER_YEAR,
2.85885980666130812e-04 * SOLAR_MASS
};
static constexpr Body uranus = {
1.28943695621391310e+01,
-1.51111514016986312e+01,
-2.23307578892655734e-01,
2.96460137564761618e-03 * DAYS_PER_YEAR,
2.37847173959480950e-03 * DAYS_PER_YEAR,
-2.96589568540237556e-05 * DAYS_PER_YEAR,
4.36624404335156298e-05 * SOLAR_MASS
};
static constexpr Body neptune = {
1.53796971148509165e+01,
-2.59193146099879641e+01,
1.79258772950371181e-01,
2.68067772490389322e-03 * DAYS_PER_YEAR,
1.62824170038242295e-03 * DAYS_PER_YEAR,
-9.51592254519715870e-05 * DAYS_PER_YEAR,
5.15138902046611451e-05 * SOLAR_MASS
};
constexpr void offset_momentum(System& bodies)
{
double px = 0.0;
double py = 0.0;
double pz = 0.0;
static_for<0, N_BODIES>([&](auto i) {
px += bodies[i].vx * bodies[i].mass;
py += bodies[i].vy * bodies[i].mass;
pz += bodies[i].vz * bodies[i].mass;
});
bodies[0].vx = -px / SOLAR_MASS;
bodies[0].vy = -py / SOLAR_MASS;
bodies[0].vz = -pz / SOLAR_MASS;
}
constexpr void advance(System& bodies, double dt)
{
static_for<0, N_BODIES>([&](auto i) {
static_for<i + 1, N_BODIES>([&](auto j) {
double dx = bodies[i].x - bodies[j].x;
double dy = bodies[i].y - bodies[j].y;
double dz = bodies[i].z - bodies[j].z;
double dSquared = dx * dx + dy * dy + dz * dz;
double mag = dt / (dSquared * std::sqrt(dSquared));
bodies[i].vx -= dx * bodies[j].mass * mag;
bodies[i].vy -= dy * bodies[j].mass * mag;
bodies[i].vz -= dz * bodies[j].mass * mag;
bodies[j].vx += dx * bodies[i].mass * mag;
bodies[j].vy += dy * bodies[i].mass * mag;
bodies[j].vz += dz * bodies[i].mass * mag;
});
});
static_for<0, N_BODIES>([&](auto i) {
bodies[i].x += dt * bodies[i].vx;
bodies[i].y += dt * bodies[i].vy;
bodies[i].z += dt * bodies[i].vz;
});
}
constexpr double energy(const System& bodies)
{
double e = 0.0;
static_for<0, N_BODIES>([&](auto i) {
const Body& iBody = bodies[i];
e += 0.5 * iBody.mass * (iBody.vx * iBody.vx + iBody.vy * iBody.vy + iBody.vz * iBody.vz);
static_for<i + 1, N_BODIES>([&](auto j) {
double dx = iBody.x - bodies[j].x;
double dy = iBody.y - bodies[j].y;
double dz = iBody.z - bodies[j].z;
double distance = std::sqrt(dx * dx + dy * dy + dz * dz);
e -= (iBody.mass * bodies[j].mass) / distance;
});
});
return e;
}
int main(int argc, char* argv[])
{
const auto n = std::atoi(argv[1]);
System system = {
sun,
jupiter,
saturn,
uranus,
neptune
};
offset_momentum(system);
std::printf("%.9f\n", energy(system));
for (size_t i = 0; i < n; ++i)
advance(system, 0.01);
std::printf("%.9f\n", energy(system));
}
RE: Comparison QB64 compiled with Ofast and without - madscijr - 06-10-2022
(06-10-2022, 10:42 AM)Coolman Wrote: i hate the c c++ language but i must admit that it is very powerful. it's a pity that qb64 doesn't generate an optimized code like this.
The good news is, with research and feedback like yours, the developers can always improve QB64 and its C code generator. No programming language or compiler was built in a day!
RE: Comparison QB64 compiled with Ofast and without - Coolman - 06-10-2022
(06-10-2022, 12:57 PM)madscijr Wrote: (06-10-2022, 10:42 AM)Coolman Wrote: i hate the c c++ language but i must admit that it is very powerful. it's a pity that qb64 doesn't generate an optimized code like this.
The good news is, with research and feedback like yours, the developers can always improve QB64 and its C code generator. No programming language or compiler was built in a day!
indeed. qb64 can be improved to generate a more clear and efficient code...
RE: Comparison QB64 compiled with Ofast and without - Fifi - 06-13-2022
So, the question is: QB64 should it be compiled with the -O3 option by default?
Are the results the same on Linux, macOS and Windows?
What are the Pros Vs Cons?
TIA
RE: Comparison QB64 compiled with Ofast and without - madscijr - 06-13-2022
(06-13-2022, 09:18 PM)Fifi Wrote: So, the question is: QB64 should it be compiled with the -O3 option by default?
Are the results the same on Linux, macOS and Windows?
What are the Pros Vs Cons?
TIA
I think we'll need the developers to answer this one!
RE: Comparison QB64 compiled with Ofast and without - DSMan195276 - 06-14-2022
> QB64 should it be compiled with the -O3 option by default?
> Are the results the same on Linux, macOS and Windows?
> What are the Pros Vs Cons?
There's two aspect that I think need to be considered: technical, and practical.
From the technical side, I'm still hesitant to recommend it as there are some coding patterns in `libqb.cpp` that are very likely to get optimized wrong, resulting in a broken program. It does appear that this isn't as big of a problem as I first assumed it would be, as we do have several successful uses of it, but I still don't think it makes sense as a default just yet.
From the practical side, QB64 itself probably shouldn't be compiled with `-O3` due to the length of time and amount of memory it can take. I haven't tried it on Linux (I think the situation might be much better) but on Windows it ends up taking several GBs of RAM and many minutes (10 or 15) to finish compiling. For Linux and MacOS, since they have to build QB64 themselves during the setup script I think that's just too long to expect people to sit through.
RE: Comparison QB64 compiled with Ofast and without - madscijr - 06-14-2022
(06-14-2022, 01:31 AM)DSMan195276 Wrote: ...
but on Windows it ends up taking several GBs of RAM and many minutes (10 or 15) to finish compiling. For Linux and MacOS, since they have to build QB64 themselves during the setup script I think that's just too long to expect people to sit through.
Good point - when I'm developing a game, the first 1000 times I run (compile) it, it's development & unit testing, not for someone to run, so the performance benefit doesn't matter so much at this stage as the ability to develop rapidly. If I had to wait 10-15 mins every time I wanted to rerun my code to see if some minor change worked, that could be a problem. When my app is far enough along where I'm ready to compile to performance test, if it's just the one time, I can wait for it to compile. But 10-15 minutes can add up, so I would probably mainly use that feature for the build before a release.
RE: Comparison QB64 compiled with Ofast and without - Coolman - 06-14-2022
personally, i decided to compile qb64 with option O3 because the editor is faster under linux but i agree with DSMan195276, the compilation of programs is slower with this option. that said it depends on the power and memory available in your computer. if not if you have a light program, it could be ok. otherwise it's better to use the original version of qb64 and when the program is finished. consider compiling it with option O3...
RE: Comparison QB64 compiled with Ofast and without - Coolman - 08-15-2022
This code has many calculation functions, different nesting, use of for next loop, select case, intensive string manipulation, function call and even use of goto instruction from basic language. It's ideal to compare processing speed with qb64 compiled in O3 and without.
more information here : https://qb64phoenix.com/forum/showthread.php?tid=770
to get 3.14, i used 750. deactivate print in the processing loop to speed up the processing. here is the result :
928.2x seconds : program compiled with qb64 (v0.5.0) -O3
1481.6x seconds : program compiled with qb64 (v0.5.0) original
Code: (Select All) Dim Shared betatest%: betatest% = -1
Width 160, 42
_Delay 0.2
_ScreenMove _Middle
Color 14, 0: Print "Wait...": Color 7, 0
limit&& = 500
j = -1
start = Timer(.001)
For i = 1 To 750
j = j + 2
If oldd$ = "" Then
d$ = "1": oldd$ = "1": oldn$ = "1": n$ = "1"
Else
d$ = LTrim$(Str$(j))
' 2nd denominator * 1st numerator.
a$ = d$: b$ = oldn$: op$ = "*"
Call string_math(a$, op$, b$, x$, limit&&)
m1$ = x$
' 1st denominator * 2nd numerator.
a$ = oldd$: b$ = n$
Call string_math(a$, op$, b$, x$, limit&&)
m2$ = x$
' Get common denominator
a$ = d$: b$ = oldd$
Call string_math(a$, op$, b$, x$, limit&&)
d$ = x$
a$ = m1$: b$ = m2$: If i / 2 = i \ 2 Then op$ = "-" Else op$ = "+"
Call string_math(a$, op$, b$, x$, limit&&)
Rem PRINT "oldn$ = "; oldn$; " oldd$ = "; oldd$, "n$ = "; x$; " d$ = "; d$
'Print "n$ = "; x$; " d$ = "; d$;: Color 14, 0: Print j: Color 7, 0
oldn$ = x$: oldd$ = d$
End If
Next
Rem CALL greatest_common_factor(x$, d$, limit&&) ' Too slow.
n$ = x$
a$ = x$: b$ = d$: op$ = "/"
'speed reduction
If Len(a$) > 16 Then
j = Len(a$)
k = Len(b$)
i = j - k
a$ = Mid$(a$, 1, 16 + i): b$ = Mid$(b$, 1, 16)
End If
Call string_math(a$, op$, b$, x$, limit&&)
a$ = x$: b$ = "4": op$ = "*"
Call string_math(a$, op$, b$, x$, limit&&)
Print: Print "pi = "; x$
Color 3: Print: Print Timer(.001) - start; "seconds"
End
Sub string_math (stringmatha$, operator$, stringmathb$, runningtotal$, limit&&)
Select Case operator$
Case "+", "-"
GoTo string_add_subtract
Case "*"
GoTo string_multiply
Case "/"
GoTo string_divide
Case Else
Print "Error, no operator selected. operator$ = "; operator$
End Select
string_divide:
divsign% = 0: divremainder& = 0: divremainder$ = "": divplace& = 0: divplace2& = 0: quotient$ = "": divcarry& = 0
operationdivision% = -1
divbuffer& = Len(stringmathb$) - Len(stringmatha$)
If divbuffer& < 0 Then divbuffer& = 0
d2dividend$ = stringmatha$
d1divisor$ = stringmathb$
If Left$(d1divisor$, 1) = "0" And Len(d1divisor$) = 1 Then Print "Division by zero not allowed.": divsign% = 0: operationdivision% = 0: Exit Sub
If Left$(d1divisor$, 1) = "-" Then divsign% = -1: d1divisor$ = Mid$(d1divisor$, 2)
If Left$(d2dividend$, 1) = "-" Then
If divsign% Then
divsign% = 0
Else
divsign% = -1
End If
d2dividend$ = Mid$(d2dividend$, 2)
End If
If InStr(d1divisor$, ".") <> 0 Then
Do Until Right$(d1divisor$, 1) <> "0"
d1divisor$ = Mid$(d1divisor$, 1, Len(d1divisor$) - 1) ' Strip off trailing zeros
Loop
divplace& = Len(d1divisor$) - InStr(d1divisor$, ".")
d1divisor$ = Mid$(d1divisor$, 1, InStr(d1divisor$, ".") - 1) + Mid$(d1divisor$, InStr(d1divisor$, ".") + 1) ' Strip off decimal point.
Do Until Left$(d1divisor$, 1) <> "0"
d1divisor$ = Mid$(d1divisor$, 2) ' Strip off leading zeros for divisors smaller than .1
Loop
End If
If InStr(d2dividend$, ".") <> 0 Then
d2dividend$ = d2dividend$ + String$(divplace& - Len(d2dividend$) - InStr(d2dividend$, "."), "0") ' Add any zeros based on the length of dividend at decimal - length of divisor at decimal. If less than zero, nothing added.
divplace2& = InStr(d2dividend$, ".")
Do Until Right$(d2dividend$, 1) <> "0"
d2dividend$ = Mid$(d2dividend$, 1, Len(d2dividend$) - 1) ' Strip off trailing zeros
Loop
d2dividend$ = Mid$(d2dividend$, 1, InStr(d2dividend$, ".") - 1) + Mid$(d2dividend$, InStr(d2dividend$, ".") + 1) ' Strip off decimal point.
Else
d2dividend$ = d2dividend$ + String$(divplace&, "0") ' Add any zeros based on the length of dividend at decimal - length of divisor at decimal. If less than zero, nothing added.
divplace& = 0
End If
Do
Do
divremainder& = divremainder& + 1: divremainder$ = divremainder$ + Mid$(d2dividend$, divremainder&, 1)
If Mid$(d2dividend$, divremainder&, 1) = "" Then
If divremainder$ = String$(Len(divremainder$), "0") And Len(quotient$) > Len(d2dividend$) Then divflag% = -1: Exit Do
divcarry& = divcarry& + 1
If divcarry& = 1 Then divplace3& = divremainder& - 1
If divcarry& > limit&& + 1 + divbuffer& Then
divflag% = -2: Exit Do
End If
divremainder$ = divremainder$ + "0" ' No more digits to bring down.
End If
If Len(divremainder$) > Len(d1divisor$) Or Len(divremainder$) = Len(d1divisor$) And divremainder$ >= d1divisor$ Then Exit Do
quotient$ = quotient$ + "0"
Loop
If divflag% Then divflag% = 0: Exit Do
For div_i% = 9 To 1 Step -1
stringmatha$ = LTrim$(Str$(div_i%)): stringmathb$ = d1divisor$
m_product$ = "": GoSub string_multiply
tempcutd$ = divremainder$ ' divremainder$ can be 00 or other leading zero values.
Do
If Len(tempcutd$) = 1 Then Exit Do
If Left$(tempcutd$, 1) = "0" Then
tempcutd$ = Mid$(tempcutd$, 2)
Else
Exit Do
End If
Loop
If Len(tempcutd$) > Len(m_product$) Or Len(tempcutd$) = Len(m_product$) And m_product$ <= tempcutd$ Then Exit For
Next
quotient$ = quotient$ + LTrim$(Str$(div_i%))
stringmatha$ = LTrim$(Str$(div_i%)): stringmathb$ = d1divisor$
m_product$ = "": GoSub string_multiply
operator$ = "-"
stringmatha$ = divremainder$
stringmathb$ = m_product$
GoSub string_add_subtract
divremainder$ = stringmatha$
operator$ = "/"
Loop
If divplace& = 0 And divplace2& = 0 Then divplace& = divplace3&
If divplace2& Then divplace& = divplace& + divplace2& - 1
If quotient$ = "" Then divplace& = 0 ' dividend is zero.
If divplace& Or divplace2& Then
quotient$ = Mid$(quotient$, 1, divplace&) + "." + Mid$(quotient$, divplace& + 1)
Do Until Right$(quotient$, 1) <> "0"
quotient$ = Mid$(quotient$, 1, Len(quotient$) - 1) ' Strip off trailing zeros
Loop
If Right$(quotient$, 1) = "." Then quotient$ = Mid$(quotient$, 1, Len(quotient$) - 1) ' Strip off abandoned decimal.
End If
Do Until Left$(quotient$, 1) <> "0"
quotient$ = Mid$(quotient$, 2) ' Strip off leading zeros
Loop
If quotient$ = "" Then quotient$ = "0": divsign% = 0
operationdivision% = 0
stringmathb$ = quotient$: quotient$ = ""
If stringmathb$ = "overflow" Then divsign% = 0: operationdivision% = 0: Exit Sub
runningtotal$ = stringmathb$: stringmathb$ = ""
If divsign% Then runningtotal$ = "-" + runningtotal$
If stringmathround$ <> "" Then runningtotal$ = runningtotal$ + stringmathround$
operationdivision% = 0
Exit Sub
string_multiply:
m_decimal_places& = 0: m_product$ = ""
fac1$ = stringmatha$: fac2$ = stringmathb$ ' Make numbers whole numbers and remove any - sign.
If Left$(fac1$, 1) = "-" Then fac1$ = Mid$(fac1$, 2): m_sign% = -1
If Left$(fac2$, 1) = "-" Then fac2$ = Mid$(fac2$, 2): If m_sign% Then m_sign% = 0 Else m_sign% = -1
If InStr(fac1$, ".") <> 0 Then m_decimal_places& = Len(fac1$) - InStr(fac1$, "."): fac1$ = Mid$(fac1$, 1, InStr(fac1$, ".") - 1) + Mid$(fac1$, InStr(fac1$, ".") + 1)
If InStr(fac2$, ".") <> 0 Then m_decimal_places& = m_decimal_places& + Len(fac2$) - InStr(fac2$, "."): fac2$ = Mid$(fac2$, 1, InStr(fac2$, ".") - 1) + Mid$(fac2$, InStr(fac2$, ".") + 1)
For m_i& = Len(fac2$) To 1 Step -1 ' Multiply each charater top and bottom.
m_k& = m_l&
m_x2$ = Mid$(fac2$, m_i&, 1)
For m_j& = Len(fac1$) To 1 Step -1
m_x1$ = Mid$(fac1$, m_j&, 1)
If m_product$ <> "" Then
m_add$ = LTrim$(Str$(Val(m_x1$) * Val(m_x2$))) + String$(m_k&, "0")
m_t& = 0: m_xproduct$ = "": m_carry% = 0
Do ' Add multiplied characters together.
m_x3$ = Mid$(m_add$, Len(m_add$) - m_t&, 1)
m_x4$ = Mid$(m_product$, Len(m_product$) - m_t&, 1)
If m_x3$ = "" And m_x4$ = "" Then
If m_carry% Then m_xproduct$ = "1" + m_xproduct$
Exit Do
End If
m_g% = Val(m_x3$) + Val(m_x4$) + m_carry%
If m_g% >= 10 Then m_g% = m_g% - 10: m_carry% = 1 Else m_carry% = 0
m_xproduct$ = LTrim$(Str$(m_g%)) + m_xproduct$
m_t& = m_t& + 1
Loop
m_product$ = m_xproduct$: m_xproduct$ = ""
Else
m_product$ = LTrim$(Str$(Val(m_x1$) * Val(m_x2$))) + String$(m_k&, "0") ' First loop makes variable here.
End If
m_k& = m_k& + 1 ' Adds trailing zeros multiplication
Next
m_l& = m_l& + 1 ' Used to reset value for m_k& adding one trailing zer for each loop.
Next
fac1$ = "": fac2$ = "": m_l& = 0: m_k& = 0: m_t& = 0
If m_decimal_places& > Len(m_product$) Then m_product$ = String$(m_decimal_places& - Len(m_product$), "0") + m_product$ ' Add any leading zeros to a decimal. Ex: .02 * .01 is factored as 002. It needs one leading zero before adding the decimal point, .0002.
If m_decimal_places& And m_product$ <> "0" Then ' Replace any decimal point.
m_product$ = Mid$(m_product$, 1, Len(m_product$) - m_decimal_places&) + "." + Mid$(m_product$, Len(m_product$) - m_decimal_places& + 1)
End If
Do Until Left$(m_product$, 1) <> "0" ' Remove leading zeros.
m_product$ = Mid$(m_product$, 2)
Loop
If m_decimal_places& Then
Do Until Right$(m_product$, 1) <> "0" ' Remove trailing zeros in a decimal sum.
m_product$ = Mid$(m_product$, 1, Len(m_product$) - 1)
Loop
End If
If m_product$ = "" Then m_product$ = "0": m_sign% = 0
If Right$(m_product$, 1) = "." Then m_product$ = Mid$(m_product$, 1, Len(m_product$) - 1) ' Remove decimal from the end of an integer total.
If operationdivision% Then m_sign% = 0: Return
stringmathb$ = m_product$: m_product$ = ""
If stringmathb$ = "overflow" Then Exit Sub
runningtotal$ = stringmathb$: stringmathb$ = ""
If m_sign% Then runningtotal$ = "-" + runningtotal$: m_sign% = 0
Exit Sub
string_add_subtract:
If InStr(stringmatha$, ".") <> 0 Then ' Evaluate sum for decimal fraction.
sumplace& = Len(stringmatha$) - InStr(stringmatha$, ".")
stringmatha$ = Mid$(stringmatha$, 1, InStr(stringmatha$, ".") - 1) + Mid$(stringmatha$, InStr(stringmatha$, ".") + 1) ' Strip out decimal
End If
If InStr(stringmathb$, ".") <> 0 Then ' Evaluate number for decimal fraction.
numplace& = Len(stringmathb$) - InStr(stringmathb$, ".")
stringmathb$ = Mid$(stringmathb$, 1, InStr(stringmathb$, ".") - 1) + Mid$(stringmathb$, InStr(stringmathb$, ".") + 1) ' Strip out decimal
End If
If sumplace& > numplace& Then addsubplace& = sumplace& Else addsubplace& = numplace&
If sumplace& > addsubplace& Then
stringmatha$ = stringmatha$ + String$(sumplace& - addsubplace&, "0")
ElseIf addsubplace& > sumplace& Then
stringmatha$ = stringmatha$ + String$(addsubplace& - sumplace&, "0")
End If
If numplace& > addsubplace& Then
stringmathb$ = stringmathb$ + String$(numplace& - addsubplace&, "0")
ElseIf addsubplace& > numplace& Then
stringmathb$ = stringmathb$ + String$(addsubplace& - numplace&, "0")
End If ' END Decimal evaluations.
If Left$(stringmatha$, 1) = "-" Then sign_input$ = "-" Else sign_input$ = "+"
If Left$(stringmathb$, 1) = "-" Then sign_total$ = "-" Else sign_total$ = "+"
addsubsign% = 0
Select Case sign_input$ + operator$ + sign_total$
Case "+++", "+--"
operator$ = "+"
If Left$(stringmathb$, 1) = "-" Then stringmathb$ = Mid$(stringmathb$, 2)
Case "++-", "+-+"
operator$ = "-"
If Left$(stringmathb$, 1) = "-" Then stringmathb$ = Mid$(stringmathb$, 2)
GoSub string_comp
If gl% < 0 Then Swap stringmatha$, stringmathb$: addsubsign% = -1
Case "---", "-++"
operator$ = "-"
If Left$(stringmatha$, 1) = "-" Then stringmatha$ = Mid$(stringmatha$, 2)
If Left$(stringmathb$, 1) = "-" Then stringmathb$ = Mid$(stringmathb$, 2)
GoSub string_comp
If gl% < 0 Then Swap stringmatha$, stringmathb$: addsubsign% = -1
Case "--+", "-+-"
operator$ = "+"
If Left$(stringmatha$, 1) = "-" Then stringmatha$ = Mid$(stringmatha$, 2)
If Left$(stringmathb$, 1) = "-" Then stringmathb$ = Mid$(stringmathb$, 2)
addsubsign% = -1
End Select
If Len(stringmatha$) > Len(stringmathb$) Then
stringmathb$ = String$(Len(stringmatha$) - Len(stringmathb$), "0") + stringmathb$
ElseIf Len(stringmatha$) < Len(stringmathb$) Then
stringmatha$ = String$(Len(stringmathb$) - Len(stringmatha$), "0") + stringmatha$
End If
addsubx1$ = ""
Select Case operator$
Case "+", "="
For addsubii& = Len(stringmatha$) To 1 Step -1
addsubx1% = Val(Mid$(stringmatha$, addsubii&, 1)) + Val(Mid$(stringmathb$, addsubii&, 1)) + addsubcarry%
If addsubx1% > 9 Then addsubx1% = addsubx1% - 10: addsubcarry% = 1 Else addsubcarry% = 0
addsubx1$ = LTrim$(Str$(addsubx1%)) + addsubx1$
Next
If addsubcarry% Then addsubx1$ = "1" + addsubx1$: addsubcarry% = 0
GoSub replace_decimal
Case "-"
For addsubii& = Len(stringmatha$) To 1 Step -1
addsubx1% = Val(Mid$(stringmatha$, addsubii&, 1)) - Val(Mid$(stringmathb$, addsubii&, 1)) + addsubcarry%
If addsubx1% < 0 Then addsubx1% = addsubx1% + 10: addsubcarry% = -1 Else addsubcarry% = 0
addsubx1$ = LTrim$(Str$(addsubx1%)) + addsubx1$
Next
If addsubx1$ <> "" And addsubx1$ <> String$(Len(addsubx1$), "0") Then GoSub replace_decimal
Do Until Left$(addsubx1$, 1) <> "0" ' Remove leading zeros.
addsubx1$ = Mid$(addsubx1$, 2)
Loop
If addsubx1$ = "" Then
addsubx1$ = "0": addsubsign% = 0
Else
If addsubcarry% Then addsubx1$ = "-" + addsubx1$: addsubcarry% = 0
End If
End Select
If addsubsign% Then
If Left$(addsubx1$, 1) = "-" Then addsubx1$ = Mid$(addsubx1$, 2) Else addsubx1$ = "-" + addsubx1$
End If
stringmatha$ = addsubx1$: addsubx1$ = ""
If operationdivision% Then Return
stringmathb$ = stringmatha$: stringmatha$ = ""
If Left$(stringmathb$, 1) = "-" Then
stringmathb$ = Mid$(stringmathb$, 2)
n2sign$ = "-"
Else
n2sign$ = ""
End If
If stringmathb$ = "overflow" Then n2sign$ = "": Exit Sub
runningtotal$ = n2sign$ + stringmathb$: n2sign$ = ""
Exit Sub
replace_decimal:
If addsubplace& Then
addsubx1$ = String$(addsubplace& - Len(addsubx1$), "0") + addsubx1$
addsubx1$ = Mid$(addsubx1$, 1, Len(addsubx1$) - addsubplace&) + "." + Mid$(addsubx1$, Len(addsubx1$) - addsubplace& + 1)
Do Until Right$(addsubx1$, 1) <> "0" ' Remove trailing zeros in a decimal sum.
addsubx1$ = Mid$(addsubx1$, 1, Len(addsubx1$) - 1)
addsubplace& = addsubplace& - 1
Loop
If Right$(addsubx1$, 1) = "." Then addsubx1$ = Mid$(addsubx1$, 1, Len(addsubx1$) - 1) ' Number is now an integer.
End If
Return
string_comp:
Do
Rem Remove trailing zeros after a decimal point.
If InStr(acomp$, ".") Then
Do Until Right$(acomp$, 1) <> "0" And Right$(acomp$, 1) <> "." And Right$(acomp$, 1) <> "-"
acomp$ = Mid$(acomp$, 1, Len(acomp$) - 1)
Loop
End If
If InStr(bcomp$, ".") Then
Do Until Right$(bcomp$, 1) <> "0" And Right$(bcomp$, 1) <> "." And Right$(bcomp$, 1) <> "-"
bcomp$ = Mid$(bcomp$, 1, Len(bcomp$) - 1)
Loop
End If
If Mid$(acomp$, 1, 2) = "-0" Or acomp$ = "" Or acomp$ = "-" Then acomp$ = "0"
If Mid$(bcomp$, 1, 2) = "-0" Or bcomp$ = "" Or bcomp$ = "-" Then bcomp$ = "0"
' A - and +
If Left$(acomp$, 1) = "-" Then j% = -1
If Left$(bcomp$, 1) = "-" Then k% = -1
If k% = 0 And j% Then gl% = -1: Exit Do
If j% = 0 And k% Then gl% = 1: Exit Do
' A decimal and non-decimal.
j% = InStr(acomp$, ".")
k% = InStr(bcomp$, ".")
If j% = 0 And k% Then
If acomp$ = "0" Then gl% = -1 Else gl% = 1
Exit Do
End If
If k% = 0 And j% Then
If bcomp$ = "0" Then gl% = 1 Else gl% = -1
Exit Do
End If
' Both decimals.
If j% Then
If acomp$ > bcomp$ Then
gl% = 1
ElseIf acomp$ = bcomp$ Then gl% = 0
ElseIf acomp$ < bcomp$ Then gl% = -1
End If
Exit Do
End If
' Both positive or both negative whole numbers.
Select Case Len(acomp$)
Case Is < Len(bcomp$)
gl% = -1
Case Is = Len(bcomp$)
If acomp$ = bcomp$ Then
gl% = 0
ElseIf acomp$ > bcomp$ Then gl% = 1
ElseIf acomp$ < bcomp$ Then gl% = -1
End If
Case Is > Len(bcomp$)
gl% = 1
End Select
Exit Do
Loop
Return
End Sub
Sub greatest_common_factor (gfca$, gfcb$, limit&&)
hold_gfca$ = gfca$
hold_gfcb$ = gfcb$
numerator$ = gfca$: denominator$ = gfcb$
' Make both numbers positive.
If Mid$(gfca$, 1, 1) = "-" Then gfca$ = Mid$(gfca$, 2)
If Mid$(gfcb$, 1, 1) = "-" Then gfcb$ = Mid$(gfcb$, 2)
GoSub string_comp
If gl% Then Swap gfca$, gfcb$
Do
stringmatha$ = gfca$: stringmathb$ = gfcb$
operator$ = "/": Call string_math(stringmatha$, operator$, stringmathb$, runningtotal$, limit&&)
If InStr(runningtotal$, ".") Then runningtotal$ = Mid$(runningtotal$, 1, InStr(runningtotal$, ".") - 1)
stringmatha$ = runningtotal$: stringmathb$ = gfcb$
operator$ = "*": Call string_math(stringmatha$, operator$, stringmathb$, runningtotal$, limit&&)
stringmatha$ = gfca$: stringmathb$ = runningtotal$
operator$ = "-": Call string_math(stringmatha$, operator$, stringmathb$, runningtotal$, limit&&)
Swap gfca$, gfcb$: gfcb$ = runningtotal$
If runningtotal$ = "0" Then Exit Do
Loop
stringmatha$ = numerator$: stringmathb$ = gfca$
operator$ = "/": Call string_math(stringmatha$, operator$, stringmathb$, runningtotal$, limit&&)
numerator$ = runningtotal$
stringmatha$ = denominator$: stringmathb$ = gfca$
operator$ = "/": Call string_math(stringmatha$, operator$, stringmathb$, runningtotal$, limit&&)
denominator$ = runningtotal$
If betatest% Then
Print "GFC = "; gfca$; ": Previous fraction: "; hold_gfca$; " / "; hold_gfcb$, "Reduced: "; numerator$; " / "; denominator$
End If
gfca$ = numerator$: gfcb$ = denominator$
Exit Sub
string_comp:
Do
Rem Remove trailing zeros after a decimal point.
If InStr(a$, ".") Then
Do Until Right$(a$, 1) <> "0" And Right$(a$, 1) <> "." And Right$(a$, 1) <> "-"
a$ = Mid$(a$, 1, Len(a$) - 1)
Loop
End If
If InStr(b$, ".") Then
Do Until Right$(b$, 1) <> "0" And Right$(b$, 1) <> "." And Right$(b$, 1) <> "-"
b$ = Mid$(b$, 1, Len(b$) - 1)
Loop
End If
If Mid$(a$, 1, 2) = "-0" Or a$ = "" Or a$ = "-" Then a$ = "0"
If Mid$(b$, 1, 2) = "-0" Or b$ = "" Or b$ = "-" Then b$ = "0"
' A - and +
If Left$(a$, 1) = "-" Then j% = -1
If Left$(b$, 1) = "-" Then k% = -1
If k% = 0 And j% Then gl% = -1: Exit Do
If j% = 0 And k% Then gl% = 1: Exit Do
' A decimal and non-decimal.
j% = InStr(a$, ".")
k% = InStr(b$, ".")
If j% = 0 And k% Then
If a$ = "0" Then gl% = -1 Else gl% = 1
Exit Do
End If
If k% = 0 And j% Then
If b$ = "0" Then gl% = 1 Else gl% = -1
Exit Do
End If
' Both decimals.
If j% Then
If a$ > b$ Then
gl% = 1
ElseIf a$ = b$ Then gl% = 0
ElseIf a$ < b$ Then gl% = -1
End If
Exit Do
End If
' Both positive or both negative whole numbers.
Select Case Len(a$)
Case Is < Len(b$)
gl% = -1
Case Is = Len(b$)
If a$ = b$ Then
gl% = 0
ElseIf a$ > b$ Then gl% = 1
ElseIf a$ < b$ Then gl% = -1
End If
Case Is > Len(b$)
gl% = 1
End Select
Exit Do
Loop
Return
End Sub
RE: Comparison QB64 compiled with Ofast and without - Pete - 08-15-2022
Nice! Thanks for speed testing.
I developed my string math routine as a way to never get caught in "computer" binary math errors. VAL() even fails when strings get too big, so I took that into account, too. I find this to be a great system for big number accounting, probably useful for chemistry molar calculations, but too slow for the kinds of massive numbers produced, as when calculating pi.
BTW: The pi algorithm I used is based on Leibniz's formula. There are other methods, but I'm not sure which is the fastest or most accurate. What's funny is the stone age method of putting a thread around a cylinder of known diameter is faster at getting to 3.14. I imagine at 15min to get that with my routine using Leibniz calculations, it would take several hours to get anything better than 3.14159.
Pete
|