aboutsummaryrefslogtreecommitdiffstats
path: root/src/decimal.c
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2019-10-06 20:27:45 +0200
committerDavid Robillard <d@drobilla.net>2020-10-27 13:13:59 +0100
commit5a0d9e5a66e0ad0b85db3740b479301128637ad5 (patch)
treec14ac93038b406fbf9ef1eac86cf1764d611b77f /src/decimal.c
parenta5ae2f6ee17cdbd033ac2cc13e726ff5d71cabdc (diff)
downloadserd-5a0d9e5a66e0ad0b85db3740b479301128637ad5.tar.gz
serd-5a0d9e5a66e0ad0b85db3740b479301128637ad5.tar.bz2
serd-5a0d9e5a66e0ad0b85db3740b479301128637ad5.zip
Factor out decimal digit generation
Diffstat (limited to 'src/decimal.c')
-rw-r--r--src/decimal.c64
1 files changed, 63 insertions, 1 deletions
diff --git a/src/decimal.c b/src/decimal.c
index 7f78c8a7..6c0bad59 100644
--- a/src/decimal.c
+++ b/src/decimal.c
@@ -1,5 +1,5 @@
/*
- Copyright 2019 David Robillard <http://drobilla.net>
+ Copyright 2019-2020 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -18,8 +18,70 @@
#include "int_math.h"
+#include <assert.h>
+#include <float.h>
+#include <math.h>
+
int
serd_count_digits(const uint64_t i)
{
return i == 0 ? 1 : (int)serd_ilog10(i) + 1;
}
+
+unsigned
+serd_double_int_digits(const double abs)
+{
+ const double lg = ceil(log10(floor(abs) + 1.0));
+ return lg < 1.0 ? 1U : (unsigned)lg;
+}
+
+unsigned
+serd_decimals(const double d, char* const buf, const unsigned frac_digits)
+{
+ assert(isfinite(d));
+
+ // Point s to decimal point location
+ const double abs_d = fabs(d);
+ const double int_part = floor(abs_d);
+ const unsigned int_digits = serd_double_int_digits(abs_d);
+ unsigned n_bytes = 0;
+ char* s = buf + int_digits;
+ if (d < 0.0) {
+ *buf = '-';
+ ++s;
+ }
+
+ // Write integer part (right to left)
+ char* t = s - 1;
+ uint64_t dec = (uint64_t)int_part;
+ do {
+ *t-- = (char)('0' + dec % 10);
+ } while ((dec /= 10) > 0);
+
+
+ *s++ = '.';
+
+ // Write fractional part (right to left)
+ double frac_part = fabs(d - int_part);
+ if (frac_part < DBL_EPSILON) {
+ *s++ = '0';
+ n_bytes = (unsigned)(s - buf);
+ } else {
+ uint64_t frac = (uint64_t)llround(frac_part * pow(10.0, (int)frac_digits));
+ s += frac_digits - 1;
+ unsigned i = 0;
+
+ // Skip trailing zeros
+ for (; i < frac_digits - 1 && !(frac % 10); ++i, --s, frac /= 10) {}
+
+ n_bytes = (unsigned)(s - buf) + 1u;
+
+ // Write digits from last trailing zero to decimal point
+ for (; i < frac_digits; ++i) {
+ *s-- = (char)('0' + (frac % 10));
+ frac /= 10;
+ }
+ }
+
+ return n_bytes;
+}