CDE and dCBOR encode-side support (#193)
A mode that errors out if non-preferred serialization is attempted.
Layered on top of that is CDE mode that always sorts maps.
Layered on top of the dCBOR mode, that disallows a few things and unifies encoding of the float and integer number spaces. There is no change to decoding, though some are planned.
NaN Payloads can no longer be output by default. You must explicitly allow them. This is a non-compatibility with QCBOR 1.x
65-bit negative integers also cannot be output without explicitly allowing them.
* CDE and dCBOR support
* Tests for single precision and exponent boundaries
* test complete and passing; code clean up; bug fix
* Add comments for encoding modes
* tidy up and documentation
* Check point progress on CDE, preferred and dCBOR
* documentation updates
* Minor test fixes; minor code improvements
* full test fan out passes
* Formatting and documentation nits
---------
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/src/ieee754.h b/src/ieee754.h
index 863019b..53ab3eb 100644
--- a/src/ieee754.h
+++ b/src/ieee754.h
@@ -10,11 +10,11 @@
* Created on 7/23/18
* ========================================================================== */
-#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
#ifndef ieee754_h
#define ieee754_h
+
#include <stdint.h>
@@ -25,6 +25,9 @@
* smaller representation (e.g., double to single) that does not lose
* precision for CBOR preferred serialization.
*
+ * This also implements conversion of floats to whole numbers as
+ * is required for dCBOR.
+ *
* This implementation works entirely with shifts and masks and does
* not require any floating-point HW or library.
*
@@ -51,6 +54,7 @@
* conversion. This version is reduced to what is needed for CBOR.
*/
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
/**
* @brief Convert half-precision float to double-precision float.
@@ -87,6 +91,22 @@
} IEEE754_union;
+/** Holds result of an attempt to convert a floating-point
+ * number to an int64_t or uint64_t.
+ */
+struct IEEE754_ToInt {
+ enum {IEEE754_ToInt_IS_INT,
+ IEEE754_ToInt_IS_UINT,
+ IEEE754_ToInt_NO_CONVERSION,
+ IEEE754_ToInt_NaN
+ } type;
+ union {
+ uint64_t un_signed;
+ int64_t is_signed;
+ } integer;
+};
+
+
/**
* @brief Convert a double to either single or half-precision.
*
@@ -102,7 +122,7 @@
* This handles all subnormals and NaN payloads.
*/
IEEE754_union
-IEEE754_DoubleToSmaller(double d, int bAllowHalfPrecision);
+IEEE754_DoubleToSmaller(double d, int bAllowHalfPrecision, int bNoNaNPayload);
/**
@@ -118,9 +138,89 @@
* This handles all subnormals and NaN payloads.
*/
IEEE754_union
-IEEE754_SingleToHalf(float f);
+IEEE754_SingleToHalf(float f, int bNoNanPayloads);
+
+
+/**
+ * @brief Convert a double-precision float to integer if whole number
+ *
+ * @param[in] d The value to convert.
+ *
+ * @returns Either converted number or conversion status.
+ *
+ * If the value is a whole number that will fit either in a uint64_t
+ * or an int64_t, it is converted. If it is a NaN, then there is no
+ * conversion and and the fact that it is a NaN is indicated in the
+ * returned structure. If it can't be converted, then that is
+ * indicated in the returned structure.
+ *
+ * This always returns postive numbers as a uint64_t even if they will
+ * fit in an int64_t.
+ *
+ * This never fails becaue of precision, but may fail because of range.
+ */
+struct IEEE754_ToInt
+IEEE754_DoubleToInt(double d);
+
+
+/**
+ * @brief Convert a single-precision float to integer if whole number
+ *
+ * @param[in] f The value to convert.
+ *
+ * @returns Either converted number or conversion status.
+ *
+ * If the value is a whole number that will fit either in a uint64_t
+ * or an int64_t, it is converted. If it is a NaN, then there is no
+ * conversion and and the fact that it is a NaN is indicated in the
+ * returned structure. If it can't be converted, then that is
+ * indicated in the returned structure.
+ *
+ * This always returns postive numbers as a uint64_t even if they will
+ * fit in an int64_t.
+ *
+ * This never fails becaue of precision, but may fail because of range.
+ */
+struct IEEE754_ToInt
+IEEE754_SingleToInt(float f);
+
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+
+/**
+ * @brief Tests whether NaN is "quiet" vs having a payload.
+ *
+ * @param[in] dNum Double number to test.
+ *
+ * @returns 0 if a quiet NaN, 1 if it has a payload.
+ *
+ * A quiet NaN is usually represented as 0x7ff8000000000000. That is
+ * the significand bits are 0x8000000000000. If the significand bits
+ * are other than 0x8000000000000 it is considered to have a NaN
+ * payload.
+ *
+ * Note that 0x7ff8000000000000 is not specified in a standard, but it
+ * is commonly implemented and chosen by CBOR as the best way to
+ * represent a NaN.
+ */
+int
+IEEE754_IsNotStandardDoubleNaN(double dNum);
+
+
+
+/**
+ * @brief Tests whether NaN is "quiet" vs having a payload.
+ *
+ * @param[in] fNum Float number to test.
+ *
+ * @returns 0 if a quiet NaN, 1 if it has a payload.
+ *
+ * See IEEE754_IsNotStandardDoubleNaN(). A single precision quiet NaN
+ * is 0x7fc00000.
+ */
+int
+IEEE754_IsNotStandardSingleNaN(float fNum);
#endif /* ieee754_h */
-#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */