Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Examples #97

Merged
merged 2 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions examples/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
noinst_PROGRAMS = \
exprtree \
factorial128 \
sinxaprx128 \
calcdecimal128

AM_CPPFLAGS = -I${top_srcdir}/src -I../src
Expand All @@ -10,33 +11,45 @@ LDADD = ../src/libbiguint.a
CLEANFILES =

if WITH_BIGUINT256
noinst_PROGRAMS += powers10001 powers3 factorial@bits256@ calcdecimal@bits256@
CLEANFILES += factorial@[email protected] calcdecimal@[email protected]
noinst_PROGRAMS += powers10001 powers3 factorial@bits256@ sinxaprx@bits256@ calcdecimal@bits256@
CLEANFILES += factorial@[email protected] sinxaprx@[email protected] calcdecimal@[email protected]
nodist_factorial@bits256@_SOURCES = factorial@[email protected]
nodist_sinxaprx@bits256@_SOURCES = sinxaprx@[email protected]
nodist_calcdecimal@bits256@_SOURCES = calcdecimal@[email protected]

factorial256.c: factorial128.c
$(SED) 's/128/256/g' < $< > $@

sinxaprx256.c: sinxaprx128.c
$(SED) 's/128/256/g' < $< > $@

calcdecimal256.c: calcdecimal128.c
$(SED) 's/128/256/g' < $< > $@
endif


if WITH_BIGUINT384
noinst_PROGRAMS += fibratio fibratio_dec factorial@bits384@
CLEANFILES += factorial@[email protected]
noinst_PROGRAMS += fibratio fibratio_dec sinxaprx@bits384@ factorial@bits384@
CLEANFILES += sinxaprx@[email protected] factorial@[email protected]
nodist_sinxaprx@bits384@_SOURCES = sinxaprx@[email protected]
nodist_factorial@bits384@_SOURCES = factorial@[email protected]

sinxaprx384.c: sinxaprx128.c
$(SED) 's/128/384/g' < $< > $@

factorial384.c: factorial128.c
$(SED) 's/128/384/g' < $< > $@
endif

if WITH_BIGUINT512
noinst_PROGRAMS += factorial@bits512@
CLEANFILES += factorial@[email protected]
noinst_PROGRAMS += sinxaprx@bits512@ factorial@bits512@
CLEANFILES += sinxaprx@[email protected] factorial@[email protected]
nodist_sinxaprx@bits512@_SOURCES = sinxaprx@[email protected]
nodist_factorial@bits512@_SOURCES = factorial@[email protected]

sinxaprx512.c: sinxaprx128.c
$(SED) 's/128/512/g' < $< > $@

factorial512.c: factorial128.c
$(SED) 's/128/512/g' < $< > $@
endif
Expand Down
144 changes: 144 additions & 0 deletions examples/sinxaprx128.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*******************************************************
Approximation of PI using Taylor series of sin(x).

Input:
<argv[1]>: n: precision.
Output:
stderr: PI approximations
stdout: final PI approximation
Limits: works as expected if prec is less
than the half of decimal digit capacity of BigUInt128
(i.e., 18).
******************************************************/
#include "bigdecimal128.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// Writes bigdecimal to out with accompanying message
void log_bdec(FILE *out, const BigDecimal128 *a, const char *txt) {
char buf[2 * 128 + 4];
buf[bigdecimal128_print(a, buf, 2 * 128 + 3)] = 0;
fprintf(out, "%s: %s\n", txt, buf);
}

// In this multiplication the product overflow is handled in a simple way:
// as long as the result does not fit into the storage type,
// the greater factor is divided by 10, and this division is denoted in
// the eprec output parameter (it gets decreased).
// So finally, a * b * 0.1^eprec(in) ~= retv * 0.1^eprec(out) will be true.
BigUInt128 buint_mul_simple(const BigUInt128 *a, const BigUInt128 *b, UInt *eprec) {
BigUInt128 av = *a;
BigUInt128 bv = *b;
while (true) {
BigUIntPair128 pow_tmp = biguint128_dmul(&av, &bv);
if (!biguint128_eqz(&pow_tmp.second) || bigint128_ltz(&pow_tmp.first)) {
--*eprec;
if (biguint128_lt(&av, &bv)) {
bv = biguint128_div10(&bv).first;
} else {
av = biguint128_div10(&av).first;
}
} else {
av = pow_tmp.first;
break;
}
}
return av;
}

// Calculates factorial incrementally (taking (n-k)! as input for getting n!).
// If the result can be divided by 10, it is divided indeed, and this division is
// denoted by increasing the output parameter eprec.
BigUInt128 extend_factorial(const BigUInt128 *base, unsigned int base_n, unsigned int dst_n, UInt *eprec) {
BigUInt128 a = biguint128_value_of_uint(base_n + 1);
BigUInt128 retv = *base;
for (unsigned int i = base_n; i < dst_n; ++i) {
retv = buint_mul_simple(&retv, &a, eprec);
biguint128_inc(&a);
if ((i + 1) % 5 == 0) {
retv = biguint128_div10(&retv).first;
++*eprec;
}
if ((i + 1) % 25 == 0) {
retv = biguint128_div10(&retv).first;
++*eprec;
}
}
return retv;
}

// Approximates sin(x) with it Taylor series to dst_prec digits precision.
BigDecimal128 sin_series(const BigDecimal128 *x, UInt dst_prec) {
BigUInt128 one = biguint128_ctor_unit();
// this variable accumulates the Taylor serie members
BigDecimal128 retv = {biguint128_ctor_default(), dst_prec};

// x^2
UInt x2_prec = 2 * x->prec;
BigUInt128 x2_val = buint_mul_simple(&x->val, &x->val, &x2_prec);

// xi variable is calculated as xi_num / xi_denom
// both xi_num and xi_denom are defined incrementally
BigDecimal128 xi_num = *x;
BigDecimal128 xi_denom = {one, 0};

// In the series, the xi members are added/subtracted alternately.
// First, x1 is added to the sum.
bool add = true;
unsigned int i = 1;
while (true) {
BigDecimal128 xi;
buint_bool div_res = bigdecimal128_div_safe(&xi, &xi_num, &xi_denom, dst_prec);
if (!div_res) {
fprintf(stderr, "DIV overflow\n");
}
if (biguint128_eqz(&xi.val)) {
break;
}
if (add) {
retv = bigdecimal128_add(&retv, &xi);
} else {
retv = bigdecimal128_sub(&retv, &xi);
}
add = !add;

xi_num.prec += x2_prec;
xi_num.val = buint_mul_simple(&xi_num.val, &x2_val, &xi_num.prec);
xi_denom.val = extend_factorial(&xi_denom.val, i, i + 2, &xi_num.prec);
i += 2;
}
return retv;
}

int main(int argc, const char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage:\n\t%s <precision>\n", argv[0]);
return 1;
}

// calculation precision must be somewhat higher than the result precision
unsigned int prec = atoi(argv[1]) + 1;

// initial hint: PI is in interval [3.1, 3.2]
BigDecimal128 x_lo = bigdecimal128_ctor_precv((BigDecimal128){biguint128_value_of_uint(31), 1}, prec);
BigDecimal128 x_hi = bigdecimal128_ctor_precv((BigDecimal128){biguint128_value_of_uint(32), 1}, prec);

while (true) {
BigDecimal128 x_mid = (BigDecimal128){biguint128_shrv(biguint128_add(&x_lo.val, &x_hi.val), 1), prec};
if (biguint128_eq(&x_mid.val, &x_lo.val)) break;
BigDecimal128 sinx_mid = sin_series(&x_mid, prec);
log_bdec(stderr, &x_mid, "PI (aprx)");
log_bdec(stderr, &sinx_mid, "sin(x)");
if (bigint128_ltz(&sinx_mid.val)) {
x_hi = x_mid;
} else {
x_lo = x_mid;
}
}

BigDecimal128 x_res = bigdecimal128_ctor_prec(&x_lo, prec - 1);
log_bdec(stdout, &x_res, "PI");

return 0;
}
10 changes: 5 additions & 5 deletions tests/bigdecimal128_add_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@ bool test_sub0() {

bool test_sub_approx0() {
BigDecimal128 a = {
{1}, 0};
{{1}}, 0};
BigDecimal128 b = {
{2}, 0};
{{2}}, 0};
BigDecimal128 c = {
{9}, 0};
{{9}}, 0};

bool pass = true;
for (int i = 1; pass && i < 3 * 128 / 10 + 1; ++i) {
Expand All @@ -129,9 +129,9 @@ bool test_sub_approx0() {

bool test_add_safe0(bool add, bool inverta, bool invertb) {
BigDecimal128 a = {
{1}, 0};
{{1}}, 0};
BigDecimal128 b = {
{1}, 0};
{{1}}, 0};
BigDecimal128 sum;

int loops = 128;
Expand Down
Loading