aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2019-10-06 20:59:12 +0200
committerDavid Robillard <d@drobilla.net>2020-10-27 13:13:59 +0100
commitb24672bb03c30f5cb73f628362a97bb1b02d6818 (patch)
treed51d60801b2d5951cec150fe5990c72337b4aa37 /src
parent9e59a63d9b5897d9ff6d0d9936a57c3167ea9a34 (diff)
downloadserd-b24672bb03c30f5cb73f628362a97bb1b02d6818.tar.gz
serd-b24672bb03c30f5cb73f628362a97bb1b02d6818.tar.bz2
serd-b24672bb03c30f5cb73f628362a97bb1b02d6818.zip
Add support for xsd:double and xsd:float
These can be used to serialise a float or double in the shortest normalised form that can be read back in to the exact same floating point value.
Diffstat (limited to 'src')
-rw-r--r--src/node.c81
-rw-r--r--src/static_nodes.h2
2 files changed, 83 insertions, 0 deletions
diff --git a/src/node.c b/src/node.c
index 98c8e0ac..3d63361f 100644
--- a/src/node.c
+++ b/src/node.c
@@ -34,6 +34,9 @@
#include <string.h>
// Define C11 numeric constants if the compiler hasn't already
+#ifndef FLT_DECIMAL_DIG
+# define FLT_DECIMAL_DIG 9
+#endif
#ifndef DBL_DECIMAL_DIG
# define DBL_DECIMAL_DIG 17
#endif
@@ -696,6 +699,84 @@ serd_new_decimal(const double d,
return node;
}
+static SerdNode*
+serd_new_scientific(const double d,
+ const unsigned precision,
+ const SerdNode* datatype)
+{
+ const size_t type_len = serd_node_total_size(datatype);
+ const int fpclass = fpclassify(d);
+
+ if (fpclass == FP_INFINITE && d < 0) {
+ return serd_new_typed_literal("-INF", datatype);
+ } else if (fpclass == FP_INFINITE && d > 0) {
+ return serd_new_typed_literal("INF", datatype);
+ } else if (fpclass == FP_NAN) {
+ return serd_new_typed_literal("NaN", datatype);
+ } else if (fpclass == FP_ZERO) {
+ return signbit(d) ? serd_new_typed_literal("-0.0E0", datatype)
+ : serd_new_typed_literal("0.0E0", datatype);
+ }
+
+ // Get decimal digits
+ const double abs_d = fabs(d);
+ char digits[32] = {0};
+ const SerdDecimalCount count = serd_decimals(abs_d, digits, precision);
+ assert(count.count == 1 || digits[count.count - 1] != '0');
+
+ // Calculate string length and allocate node
+ const size_t len = count.count;
+ const int expt = count.expt;
+ unsigned abs_expt = (unsigned)abs(expt);
+ const unsigned abs_exp_digits = (unsigned)serd_count_digits(abs_expt);
+
+ SerdNode* node = serd_node_malloc(type_len + len + abs_exp_digits + 4,
+ SERD_HAS_DATATYPE,
+ SERD_LITERAL);
+
+ char* const buf = serd_node_buffer(node);
+ if (d < 0.0) {
+ buf[node->n_bytes++] = '-';
+ }
+
+ // Write mantissa, with decimal point after the first (normal form)
+ buf[node->n_bytes++] = digits[0];
+ buf[node->n_bytes++] = '.';
+ if (len > 1) {
+ node->n_bytes += copy_digits(buf + node->n_bytes, digits + 1, len - 1);
+ } else {
+ buf[node->n_bytes++] = '0';
+ }
+
+ // Write exponent
+ buf[node->n_bytes++] = 'E';
+ if (expt < 0) {
+ buf[node->n_bytes++] = '-';
+ }
+ char* s = buf + node->n_bytes + abs_exp_digits - 1;
+ do {
+ *s-- = (char)('0' + (abs_expt % 10));
+ } while ((abs_expt /= 10) > 0);
+ node->n_bytes += abs_exp_digits;
+
+ memcpy(serd_node_meta(node), datatype, type_len);
+ serd_node_check_padding(node);
+ return node;
+}
+
+SerdNode*
+serd_new_double(const double d)
+{
+ return serd_new_scientific(d, DBL_DECIMAL_DIG, &serd_xsd_double.node);
+}
+
+SerdNode*
+serd_new_float(const float f)
+{
+ return serd_new_scientific(
+ (double)f, FLT_DECIMAL_DIG, &serd_xsd_float.node);
+}
+
SerdNode*
serd_new_integer(int64_t i, const SerdNode* datatype)
{
diff --git a/src/static_nodes.h b/src/static_nodes.h
index f4794d72..b7770432 100644
--- a/src/static_nodes.h
+++ b/src/static_nodes.h
@@ -32,6 +32,8 @@ typedef struct StaticNode {
DEFINE_XSD_NODE(base64Binary)
DEFINE_XSD_NODE(boolean)
DEFINE_XSD_NODE(decimal)
+DEFINE_XSD_NODE(double)
+DEFINE_XSD_NODE(float)
DEFINE_XSD_NODE(integer)
#endif // SERD_STATIC_NODES_H