diff options
author | David Robillard <d@drobilla.net> | 2019-10-06 20:59:12 +0200 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2020-10-27 13:13:59 +0100 |
commit | b24672bb03c30f5cb73f628362a97bb1b02d6818 (patch) | |
tree | d51d60801b2d5951cec150fe5990c72337b4aa37 | |
parent | 9e59a63d9b5897d9ff6d0d9936a57c3167ea9a34 (diff) | |
download | serd-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.
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | serd/serd.h | 30 | ||||
-rw-r--r-- | src/node.c | 81 | ||||
-rw-r--r-- | src/static_nodes.h | 2 |
4 files changed, 114 insertions, 0 deletions
@@ -8,6 +8,7 @@ serd (1.0.1) unstable; * Add option for writing terse output without newlines * Add support for validation * Add support for writing terse collections + * Add support for xsd:float and xsd:double literals * Bring read/write interface closer to C standard * Make nodes opaque * Make serd_strtod API const-correct diff --git a/serd/serd.h b/serd/serd.h index 9dbbffa0..5a6815f8 100644 --- a/serd/serd.h +++ b/serd/serd.h @@ -725,6 +725,36 @@ serd_new_decimal(double d, const SerdNode* datatype); /** + Create a new node by serialising `d` into a normalised xsd:double string. + + The returned node will always be in normalised scientific notation, like + "1.23E4", except for NaN and negative/positive infinity, which are "NaN", + "-INF", and "INF", respectively. + + Uses the shortest possible representation that precisely describes `d`, + which has at most 17 significant digits (under 24 characters total). + + @param d Double value to serialise. + @return A literal node with datatype xsd:double. +*/ +SERD_API +SerdNode* +serd_new_double(double d); + +/** + Create a new node by serialising `f` into a normalised xsd:float string. + + Uses identical formatting to serd_new_double(), except with at most 9 + significant digits (under 14 characters total). + + @param f Float value to serialise. + @return A literal node with datatype xsd:float. +*/ +SERD_API +SerdNode* +serd_new_float(float f); + +/** Create a new node by serialising `i` into an xsd:integer string @param i Integer value to serialise. @@ -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 |