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

[ISSUE-0087]: bigdecimal add/sub safe variant #88

Merged
merged 1 commit into from
Jan 28, 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
40 changes: 40 additions & 0 deletions src/bigdecimal128.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ static inline UInt oom_lb_(UInt prec, const BigUInt128 *val);
static inline UInt oom_ub_(UInt prec, const BigUInt128 *val);
static buint_bool tune_prec_(BigUInt128 *a, UInt *aprec, UInt trg_prec, buint_bool *has_remainder);
static buint_bool compare_(const BigDecimal128 *a, const BigDecimal128 *b, buint_bool lt);
static inline buint_bool addsub_safe_(BigDecimal128 *dest, const BigDecimal128 *a, const BigDecimal128 *b, buint_bool add);

// IMPLEMENTATION
// internal functions
Expand Down Expand Up @@ -225,6 +226,37 @@ static buint_bool compare_(const BigDecimal128 *a, const BigDecimal128 *b, buint
(altz && b->prec == bprec)); // or a was truncated and for negative values this means that a became greater, thus originally a lt b.
}

static inline buint_bool addsub_safe_(BigDecimal128 *dest, const BigDecimal128 *a, const BigDecimal128 *b, buint_bool add) {
buint_bool carry = 0;
buint_bool retv = 1;
static void (*fun[])(BigUInt128 *dest, const BigUInt128 *a, const BigUInt128 *b, buint_bool * carry) = {
biguint128_adc_replace,
biguint128_sbc_replace
};
if (a->prec == b->prec) {
fun[!add](&dest->val, &a->val, &b->val, &carry);
dest->prec = a->prec;
} else {
BigDecimal128 av = *a;
BigDecimal128 bv = *b;
buint_bool altz = bigint128_ltz(&a->val);
buint_bool bltz = bigint128_ltz(&b->val);
if (altz) {
bigint128_negate_assign(&av.val);
}
if (bltz) {
bigint128_negate_assign(&bv.val);
}
retv = gen_common_hiprec_safe_(&av.prec, &bv.prec, &av.val, &bv.val);
fun[!add != (altz != bltz)](&dest->val, &av.val, &bv.val, &carry);
if (altz) {
bigint128_negate_assign(&dest->val);
}
dest->prec = av.prec;
}
return retv;
}

// interface functions
BigDecimal128 bigdecimal128_ctor_default() {
return (BigDecimal128){biguint128_ctor_default(), 0};
Expand Down Expand Up @@ -273,12 +305,20 @@ BigDecimal128 bigdecimal128_add(const BigDecimal128 *a, const BigDecimal128 *b)
return (BigDecimal128){biguint128_add(&cp.a.val, &cp.b.val), cp.prec};
}

buint_bool bigdecimal128_add_safe(BigDecimal128 *dest, const BigDecimal128 *a, const BigDecimal128 *b) {
return addsub_safe_(dest, a, b, 1);
}

BigDecimal128 bigdecimal128_sub(const BigDecimal128 *a, const BigDecimal128 *b) {
BigDecimalCommon128 cp = gen_common_prec_(a,b);

return (BigDecimal128){biguint128_sub(&cp.a.val, &cp.b.val), cp.prec};
}

buint_bool bigdecimal128_sub_safe(BigDecimal128 *dest, const BigDecimal128 *a, const BigDecimal128 *b) {
return addsub_safe_(dest, a, b, 0);
}

BigDecimal128 bigdecimal128_mul(const BigDecimal128 *a, const BigDecimal128 *b) {
return (BigDecimal128){biguint128_mul(&a->val, &b->val), a->prec + b->prec};
}
Expand Down
2 changes: 2 additions & 0 deletions src/bigdecimal128.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,13 @@ buint_bool bigdecimal128_prec_safe(BigDecimal128 *dest, const BigDecimal128 *a,
@brief Addition.
*/
BigDecimal128 bigdecimal128_add(const BigDecimal128 *a, const BigDecimal128 *b);
buint_bool bigdecimal128_add_safe(BigDecimal128 *dest, const BigDecimal128 *a, const BigDecimal128 *b);

/**
@brief Subtraction.
*/
BigDecimal128 bigdecimal128_sub(const BigDecimal128 *a, const BigDecimal128 *b);
buint_bool bigdecimal128_sub_safe(BigDecimal128 *dest, const BigDecimal128 *a, const BigDecimal128 *b);

/**
@brief Multiplication.
Expand Down
73 changes: 73 additions & 0 deletions tests/bigdecimal128_add_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,84 @@ bool test_sub0() {
return !fail;
}

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

bool pass = true;
for (int i = 1; pass && i < 3 * 128 / 10 + 1; ++i) {
c.prec = i;
b = bigdecimal128_sub(&b, &c);
pass &= bigdecimal128_lt(&a, &b);
if (!pass) {
fprintf(stderr, "Failure approximating a value at prec %u", (unsigned int) i);
}
}
return pass;
}

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

int loops = 128;
int loops_ok_ub = 3 * 128 / 10 + (128 / 100);
int loops_fail_lb = 3 * 128 / 10 + 2 + (128 / 96);

bool pass = true;

if (inverta) {
bigint128_negate_assign(&a.val);
}
if (invertb) {
bigint128_negate_assign(&b.val);
}
for (int i = 0; pass && i < loops; ++i) {
b.prec = i;
buint_bool res = add ?
bigdecimal128_add_safe(&sum, &a, &b) :
bigdecimal128_sub_safe(&sum, &a, &b);
if (i < loops_ok_ub) {
if (!res) {
fprintf(stderr, "Add/sub (%s, %s, %s) safe should work with precision %u\n", bool_to_str(add), bool_to_str(inverta), bool_to_str(invertb), (unsigned int) i);
pass = false;
}
if ((add != invertb) ? !bigdecimal128_lt(&a, &sum) : !bigdecimal128_lt(&sum, &a)) {
fprintf(stderr, "Add/sub (%s, %s, %s) safe sum error at precision %u\n", bool_to_str(add), bool_to_str(inverta), bool_to_str(invertb), (unsigned int) i);
pass = false;
}
}
if (loops_fail_lb < i) {
if (res) {
fprintf(stderr, "Add/sub (%s, %s, %s) safe should not work with precision %u\n", bool_to_str(add), bool_to_str(inverta), bool_to_str(invertb), (unsigned int) i);
pass = false;
}
}
}
return pass;
}

int main(int argc, char **argv) {

if (1 < argc) {
fprintf(stderr, "%s does not require command line arguments\n", argv[0]);
}

assert(test_add0());
assert(test_sub0());

assert(test_sub_approx0());
for (unsigned int i = 0; i < 8; ++i) {
assert(test_add_safe0(i & 4, i & 2, i & 1));
}

return 0;
}

1 change: 1 addition & 0 deletions tests/bigdecimal128_eq_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ int main(int argc, char **argv) {
assert(test_eq1());

assert(test_ltx());

return 0;
}

Loading