NaN payload bug fix; restructure some of the floating-point processing and disabling
This fixes a bug NaN payload preferred serialization where silent NaNs with payloads were incorrectly converted to quiet NaNs on some CPUs. Internal single-to-double conversions are now implemented in SW rather than using CPU.
Previously single-to-double conversion was disabled with QCBOR_DISABLE_FLOAT_HW_USE. Now it is disabled with QCBOR_DISABLE_PREFERRED_FLOAT. This is a behavior change and the reason the minor version number is incremented.
This also fixes a few error codes related to disabled floating-point features.
This updates the documentation on floating-point feature disabling.
Test coverage of floating-point is improved a lot, particularly for NaN payloads. Floating-point tests are refactored to be more clear and organized. Test against Carsten's (partial) SW library is added.
QCBOR_ERR_HALF_PRECISION_DISABLED is renamed to QCBOR_ERR_PREFERRED_FLOAT_DISABLED to be more accurate. The old name it retained for backwards compatibility.
Left over redundant declaration of QCBOREncode_AddBytes() and some others was removed.
Error out when using AddDouble() and AddFloat() in dCBOR mode with QCBOR_DISABLE_PREFFERED_FLOAT since it won't be able to produce correct dCBOR.
Some reduction in code size for single-precision decoding.
Corrected error codes for the various floating-point disable #defines
* Improve test coverage of NaNs
* Correct NaN payload decoding with SW; NaN test coverage
* NaN Payload Revision
* #ifdef fanout should work
* Fan out works; some rearranging of code in file
* Adjust test cases for floats that now succeed without HW
* Fixes so ifdef fanout works
* Lots more testing...
* Lots of documentation and other fixes
* Missed a file
* Size optimization; more tests ...
* Fix nits
* Correct some error codes
* more test cases; code tidy
* Improve code structure for float decode
---------
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/test/float_tests.c b/test/float_tests.c
index d36ef61..302b328 100644
--- a/test/float_tests.c
+++ b/test/float_tests.c
@@ -1,7 +1,7 @@
/* ==========================================================================
* float_tests.c -- tests for float and conversion to/from half-precision
*
- * Copyright (c) 2018-2024, Laurence Lundblade. All rights reserved.
+ * Copyright (c) 2018-2025, Laurence Lundblade. All rights reserved.
* Copyright (c) 2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
@@ -18,9 +18,16 @@
#include "qcbor/qcbor_decode.h"
#include "qcbor/qcbor_spiffy_decode.h"
#include "qcbor/qcbor_number_decode.h"
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
#include <math.h> /* For INFINITY and NAN and isnan() */
+#endif
+/* This is off because it is affected by varying behavior of CPUs,
+ * compilers and float libraries. Particularly the qNaN bit
+ */
+//#define QCBOR_COMPARE_TO_HW_CONVERSION
+
/* Make a test results code that includes three components. Return code
* is xxxyyyzzz where zz is the error code, yy is the test number and
@@ -38,14 +45,261 @@
}
-#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
-
#include "half_to_double_from_rfc7049.h"
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
+
+
+/* ----- Half Precsion ----------- */
+#define HALF_NUM_SIGNIFICAND_BITS (10)
+#define HALF_NUM_EXPONENT_BITS (5)
+#define HALF_NUM_SIGN_BITS (1)
+
+#define HALF_SIGNIFICAND_SHIFT (0)
+#define HALF_EXPONENT_SHIFT (HALF_NUM_SIGNIFICAND_BITS)
+#define HALF_SIGN_SHIFT (HALF_NUM_SIGNIFICAND_BITS + HALF_NUM_EXPONENT_BITS)
+
+#define HALF_SIGNIFICAND_MASK (0x3ffU) // The lower 10 bits
+#define HALF_EXPONENT_MASK (0x1fU << HALF_EXPONENT_SHIFT) // 0x7c00 5 bits of exponent
+#define HALF_SIGN_MASK (0x01U << HALF_SIGN_SHIFT) // 0x8000 1 bit of sign
+#define HALF_QUIET_NAN_BIT (0x01U << (HALF_NUM_SIGNIFICAND_BITS-1)) // 0x0200
+
+/* Biased Biased Unbiased Use
+ * 0x00 0 -15 0 and subnormal
+ * 0x01 1 -14 Smallest normal exponent
+ * 0x1e 30 15 Largest normal exponent
+ * 0x1F 31 16 NaN and Infinity */
+#define HALF_EXPONENT_BIAS (15)
+#define HALF_EXPONENT_MAX (HALF_EXPONENT_BIAS) // 15 Unbiased
+#define HALF_EXPONENT_MIN (-HALF_EXPONENT_BIAS+1) // -14 Unbiased
+#define HALF_EXPONENT_ZERO (-HALF_EXPONENT_BIAS) // -15 Unbiased
+#define HALF_EXPONENT_INF_OR_NAN (HALF_EXPONENT_BIAS+1) // 16 Unbiased
+
+
+/* ------ Single-Precision -------- */
+#define SINGLE_NUM_SIGNIFICAND_BITS (23)
+#define SINGLE_NUM_EXPONENT_BITS (8)
+#define SINGLE_NUM_SIGN_BITS (1)
+
+#define SINGLE_SIGNIFICAND_SHIFT (0)
+#define SINGLE_EXPONENT_SHIFT (SINGLE_NUM_SIGNIFICAND_BITS)
+#define SINGLE_SIGN_SHIFT (SINGLE_NUM_SIGNIFICAND_BITS + SINGLE_NUM_EXPONENT_BITS)
+
+#define SINGLE_SIGNIFICAND_MASK (0x7fffffU) // The lower 23 bits
+#define SINGLE_EXPONENT_MASK (0xffU << SINGLE_EXPONENT_SHIFT) // 8 bits of exponent
+#define SINGLE_SIGN_MASK (0x01U << SINGLE_SIGN_SHIFT) // 1 bit of sign
+#define SINGLE_QUIET_NAN_BIT (0x01U << (SINGLE_NUM_SIGNIFICAND_BITS-1))
+
+/* Biased Biased Unbiased Use
+ * 0x0000 0 -127 0 and subnormal
+ * 0x0001 1 -126 Smallest normal exponent
+ * 0x7f 127 0 1
+ * 0xfe 254 127 Largest normal exponent
+ * 0xff 255 128 NaN and Infinity */
+#define SINGLE_EXPONENT_BIAS (127)
+#define SINGLE_EXPONENT_MAX (SINGLE_EXPONENT_BIAS)
+#define SINGLE_EXPONENT_MIN (-SINGLE_EXPONENT_BIAS+1)
+#define SINGLE_EXPONENT_ZERO (-SINGLE_EXPONENT_BIAS)
+#define SINGLE_EXPONENT_INF_OR_NAN (SINGLE_EXPONENT_BIAS+1)
+
+#define SINGLE_NAN_BITS SINGLE_EXPONENT_MASK /* NAN bits except payload */
+#define SINGLE_QNAN 0x400000
+#define SINGLE_SNAN 0x000000
+
+
+/* --------- Double-Precision ---------- */
+#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
+#define DOUBLE_NUM_EXPONENT_BITS (11)
+#define DOUBLE_NUM_SIGN_BITS (1)
+
+#define DOUBLE_SIGNIFICAND_SHIFT (0)
+#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
+#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
+
+#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
+#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
+#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
+#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
+
+
+/* Biased Biased Unbiased Use
+ * 0x00000000 0 -1023 0 and subnormal
+ * 0x00000001 1 -1022 Smallest normal exponent
+ * 0x000007fe 2046 1023 Largest normal exponent
+ * 0x000007ff 2047 1024 NaN and Infinity */
+#define DOUBLE_EXPONENT_BIAS (1023)
+#define DOUBLE_EXPONENT_MAX (DOUBLE_EXPONENT_BIAS)
+#define DOUBLE_EXPONENT_MIN (-DOUBLE_EXPONENT_BIAS+1)
+#define DOUBLE_EXPONENT_ZERO (-DOUBLE_EXPONENT_BIAS)
+#define DOUBLE_EXPONENT_INF_OR_NAN (DOUBLE_EXPONENT_BIAS+1)
+
+#define DOUBLE_NAN_BITS DOUBLE_EXPONENT_MASK /* NAN bits except payload */
+#define DOUBLE_QNAN 0x8000000000000ULL
+#define DOUBLE_SNAN 0x0000000000000ULL
+
+
+
+#ifdef NAN_EXPERIMENT
+#include <stdlib.h>
+#include <stdio.h>
+
+ int
+NaNExperiments(void)
+{
+ // double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
+ // double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
+ // double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
+
+
+ for(int i = 999; i < 1000; i++) {
+ uint64_t x1 = (uint64_t)rand() % SINGLE_SIGNIFICAND_MASK;
+
+ uint64_t uDub = DOUBLE_EXPONENT_MASK | (x1 << (DOUBLE_NUM_SIGNIFICAND_BITS - SINGLE_NUM_SIGNIFICAND_BITS));
+
+ double dd = UsefulBufUtil_CopyUint64ToDouble(uDub);
+
+ float ff = (float)dd;
+
+ uint32_t uu = UsefulBufUtil_CopyFloatToUint32(ff);
+
+ uint64_t x2 = uu & SINGLE_SIGNIFICAND_MASK;
+
+ if(x2 != x1) {
+ printf("%d: %llx %llx %llx %llx\n", i, x1, x2, x1 ^ x2, x1 & 0x200000);
+ }
+ }
+
+#if 0
+ float f1 = (float)dqNaN;
+ float f2 = (float)dsNaN;
+ float f3 = (float)dqNaNPayload;
+
+
+ uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
+ uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
+ uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
+
+ // Result of this on x86 is that every NaN is a qNaN. The intel
+ // CVTSD2SS instruction ignores the NaN payload and even converts
+ // a sNaN to a qNaN.
+#endif
+
+ return 0;
+}
+#endif /* NAN_EXPERIMENT */
+
+
+/* Returns 0 if OK, 1 if not */
+static int32_t
+HWCheckFloatToDouble(const uint64_t uDoubleToConvert, uint32_t uExpectedSingle)
+{
+#ifdef QCBOR_COMPARE_TO_HW_CONVERSION
+ if(uExpectedSingle) {
+ /* This test is off by default. It's purpose is to check
+ * QCBOR's mask-n-shift implementation against the HW/CPU
+ * instructions that do conversion between double and single.
+ * It is off because it is only used on occasion to verify
+ * QCBOR and because it is suspected that some HW/CPU does
+ * not implement this correctly. NaN payloads are an obscure
+ * feature. */
+ float f;
+ double d;
+ uint32_t uSingle;
+
+ d = UsefulBufUtil_CopyUint64ToDouble(uDoubleToConvert);
+
+ f = (float)d;
+
+ uSingle = UsefulBufUtil_CopyFloatToUint32(f);
+
+ if(isnan(d)) {
+ /* Some (all?) Intel CPUs always set the qNaN bit in conversion */
+ uExpectedSingle |= SINGLE_QNAN;
+ }
+
+ if(uSingle != uExpectedSingle) {
+ return 1;
+ }
+ }
+#else
+ (void)uDoubleToConvert;
+ (void)uExpectedSingle;
+#endif /* QCBOR_COMPARE_TO_HW_CONVERSION */
+
+ return 0;
+}
+
+/* Returns 0 if OK, 1 if not */
+static int32_t
+HWCheckDoubleToFloat(const uint32_t uSingleToConvert, uint64_t uExpectedDouble)
+{
+#ifdef QCBOR_COMPARE_TO_HW_CONVERSION
+ if(uExpectedDouble) {
+ /* This test is off by default. It's purpose is to check
+ * QCBOR's mask-n-shift implementation against the HW/CPU
+ * instructions that do conversion between double and single.
+ * It is off because it is only used on occasion to verify
+ * QCBOR and because it is suspected that some HW/CPU does
+ * not implement this correctly. NaN payloads are an obscure
+ * feature. */
+ float f;
+ double d2;
+ uint64_t dd;
+
+ f = UsefulBufUtil_CopyUint32ToFloat(uSingleToConvert);
+
+ d2 = (double)f;
+
+ dd = UsefulBufUtil_CopyDoubleToUint64(d2);
+
+ if(isnan(f)) {
+ /* Some (all?) Intel CPUs always set the qNaN bit in conversion */
+ uExpectedDouble |= DOUBLE_QNAN;
+ }
+
+ if(dd != uExpectedDouble ) {
+ return 1;
+ }
+ }
+#else
+ (void)uSingleToConvert;
+ (void)uExpectedDouble;
+#endif /* QCBOR_COMPARE_TO_HW_CONVERSION */
+ return 0;
+}
+
+
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+/* Returns 0 if OK, 1 if not */
+static int32_t
+CompareToCarsten(const uint64_t uDouble, const UsefulBufC TestOutput, const UsefulBufC Expected)
+{
+ if(Expected.len == 3) {
+ /* Just works for double to half now */
+ int uFloat16 = try_float16_encode(uDouble);
+ uint8_t CarstenEncoded[3];
+ CarstenEncoded[0] = 0xf9;
+ CarstenEncoded[1] = (uFloat16 & 0xff00) >> 8;
+ CarstenEncoded[2] = uFloat16 & 0xff;
+
+ UsefulBufC CarstenEncodedUB;
+ CarstenEncodedUB.len = 3;
+ CarstenEncodedUB.ptr = CarstenEncoded;
+
+ if(UsefulBuf_Compare(TestOutput, CarstenEncodedUB)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+
struct FloatTestCase {
double dNumber;
- float fNumber;
+ float fNumber; /* Only used when preferred is disabled */
UsefulBufC Preferred;
UsefulBufC NotPreferred;
UsefulBufC CDE;
@@ -77,10 +331,14 @@
*/
/* Always four lines per test case so shell scripts can process into
- * other formats. CDE and DCBOR standards are not complete yet,
- * encodings are what is expected. C string literals are used because they
- * are the shortest notation. They are used __with a length__ . Null
- * termination doesn't work because there are zero bytes.
+ * other formats.
+ *
+ * C string literals are used because they are the shortest
+ * notation. They are used __with a length__ . Null termination
+ * doesn't work because there are bytes with value zero.
+ *
+ * While the CDE and dCBOR standards are not complete as of mid-2025,
+ * they are unlikely to change, so the tests here are likely correct.
*/
static const struct FloatTestCase FloatTestCases[] = {
/* Zero */
@@ -217,7 +475,7 @@
{16777217, 0.0f,
{"\xFB\x41\x70\x00\x00\x10\x00\x00\x00", 9}, {"\xFB\x41\x70\x00\x00\x10\x00\x00\x00", 9},
{"\xFB\x41\x70\x00\x00\x10\x00\x00\x00", 9}, {"\x1A\x01\x00\x00\x01", 5}},
-
+
/* 33554430 -- exponent 24 to test single exponent boundary */
{33554430, 33554430.0f,
{"\xFA\x4B\xFF\xFF\xFF", 5}, {"\xFB\x41\x7F\xFF\xFF\xE0\x00\x00\x00", 9},
@@ -248,7 +506,7 @@
{"\xFB\x43\x3F\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x43\x3F\xFF\xFF\xFF\xFF\xFF\xFF", 9},
{"\xFB\x43\x3F\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\x1B\x00\x1F\xFF\xFF\xFF\xFF\xFF\xFF", 9}},
- /* 18014398509481982 -- exponent 53, 52 bits set in significand (double lacks precision to represent 18014398509481983) */
+ /* 18014398509481982 -- exponent 53, 52 bits set in significand (double lacks precision for 18014398509481983) */
{18014398509481982, 0,
{"\xFB\x43\x4F\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x43\x4F\xFF\xFF\xFF\xFF\xFF\xFF", 9},
{"\xFB\x43\x4F\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\x1B\x00\x3F\xFF\xFF\xFF\xFF\xFF\xFE", 9}},
@@ -257,7 +515,7 @@
{18014398509481984, 18014398509481984.0f,
{"\xFA\x5A\x80\x00\x00", 5}, {"\xFB\x43\x50\x00\x00\x00\x00\x00\x00", 9},
{"\xFA\x5A\x80\x00\x00", 5}, {"\x1B\x00\x40\x00\x00\x00\x00\x00\x00", 9}},
-
+
/* 18446742974197924000.0.0 -- largest single that can convert to uint64 */
{18446742974197924000.0, 18446742974197924000.0f,
{"\xFA\x5F\x7F\xFF\xFF", 5}, {"\xFB\x43\xEF\xFF\xFF\xE0\x00\x00\x00", 9},
@@ -333,66 +591,6 @@
};
-/* Can't use the types double and float here because there's no way in C to
- * construct arbitrary payloads for those types.
- */
-struct NaNTestCase {
- uint64_t uDouble; /* Converted to double in test */
- uint32_t uSingle; /* Converted to single in test */
- UsefulBufC Preferred;
- UsefulBufC NotPreferred;
- UsefulBufC CDE;
- UsefulBufC DCBOR;
-};
-
-/* Always four lines per test case so shell scripts can process into
- * other formats. CDE and DCBOR standards are not complete yet,
- * encodings are a guess. C string literals are used because they
- * are the shortest notation. They are used __with a length__ . Null
- * termination doesn't work because there are zero bytes.
- */
-static const struct NaNTestCase NaNTestCases[] = {
-
- /* Payload with most significant bit set, a qNaN by most implementations */
- {0x7ff8000000000000, 0x00000000,
- {"\xF9\x7E\x00", 3}, {"\xFB\x7F\xF8\x00\x00\x00\x00\x00\x00", 9},
- {"\xF9\x7E\x00", 3}, {"\xF9\x7E\x00", 3}},
-
- /* Payload with single rightmost set */
- {0x7ff8000000000001, 0x00000000,
- {"\xFB\x7F\xF8\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\x7F\xF8\x00\x00\x00\x00\x00\x01", 9},
- {"\xF9\x7E\x00", 3}, {"\xF9\x7E\x00", 3}},
-
- /* Payload with 10 leftmost bits set -- converts to half */
- {0x7ffffc0000000000, 0x00000000,
- {"\xF9\x7F\xFF", 3}, {"\xFB\x7F\xFF\xFC\x00\x00\x00\x00\x00", 9},
- {"\xF9\x7E\x00", 3}, {"\xF9\x7E\x00", 3}},
-
- /* Payload with 10 rightmost bits set -- cannot convert to half */
- {0x7ff80000000003ff, 0x00000000,
- {"\xFB\x7F\xF8\x00\x00\x00\x00\x03\xFF", 9}, {"\xFB\x7F\xF8\x00\x00\x00\x00\x03\xFF", 9},
- {"\xF9\x7E\x00", 3}, {"\xF9\x7E\x00", 3}},
-
- /* Payload with 23 leftmost bits set -- converts to a single */
- {0x7ffFFFFFE0000000, 0x7fffffff,
- {"\xFA\x7F\xFF\xFF\xFF", 5}, {"\xFB\x7F\xFF\xFF\xFF\xE0\x00\x00\x00", 9},
- {"\xF9\x7E\x00", 3}, {"\xF9\x7E\x00", 3}},
-
- /* Payload with 24 leftmost bits set -- fails to convert to a single */
- {0x7ffFFFFFF0000000, 0x00000000,
- {"\xFB\x7F\xFF\xFF\xFF\xF0\x00\x00\x00", 9}, {"\xFB\x7F\xFF\xFF\xFF\xF0\x00\x00\x00", 9},
- {"\xF9\x7E\x00", 3}, {"\xF9\x7E\x00", 3}},
-
- /* Payload with all bits set */
- {0x7fffffffffffffff, 0x00000000,
- {"\xFB\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9},
- {"\xF9\x7E\x00", 3}, {"\xF9\x7E\x00", 3}},
-
- /* List terminator */
- {0, 0, {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0} }
-};
-
-
/* Public function. See float_tests.h
*
* This is the main test of floating-point encoding / decoding. It is
@@ -405,7 +603,6 @@
{
unsigned int uTestIndex;
const struct FloatTestCase *pTestCase;
- const struct NaNTestCase *pNaNTestCase;
MakeUsefulBufOnStack( TestOutBuffer, 20);
UsefulBufC TestOutput;
QCBOREncodeContext EnCtx;
@@ -413,19 +610,16 @@
QCBORDecodeContext DCtx;
QCBORItem Item;
uint64_t uDecoded;
-#ifdef QCBOR_DISABLE_FLOAT_HW_USE
- uint32_t uDecoded2;
-#endif
- /* Test a variety of doubles */
+ /* Test a variety of doubles and some singles */
for(uTestIndex = 0; FloatTestCases[uTestIndex].Preferred.len != 0; uTestIndex++) {
pTestCase = &FloatTestCases[uTestIndex];
- if(uTestIndex == 48) {
+ if(uTestIndex == 16) {
uDecoded = 1;
}
- /* Number Encode of Preferred */
+ /* Preferred Encode */
QCBOREncode_Init(&EnCtx, TestOutBuffer);
QCBOREncode_AddDouble(&EnCtx, pTestCase->dNumber);
uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
@@ -433,276 +627,588 @@
if(uErr != QCBOR_SUCCESS) {
return MakeTestResultCode(uTestIndex, 1, uErr);;
}
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
if(UsefulBuf_Compare(TestOutput, pTestCase->Preferred)) {
- return MakeTestResultCode(uTestIndex, 1, 200);
+ return MakeTestResultCode(uTestIndex, 2, 200);
}
- /* Number Encode of Not Preferred */
+ if(CompareToCarsten(UsefulBufUtil_CopyDoubleToUint64(pTestCase->dNumber), TestOutput, pTestCase->Preferred)) {
+ return MakeTestResultCode(uTestIndex, 202, 200);
+ }
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+ if(UsefulBuf_Compare(TestOutput, pTestCase->NotPreferred)) {
+ return MakeTestResultCode(uTestIndex, 3, 200);
+ }
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+ /* Non-Preferred Encode */
QCBOREncode_Init(&EnCtx, TestOutBuffer);
QCBOREncode_AddDoubleNoPreferred(&EnCtx, pTestCase->dNumber);
uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
if(uErr != QCBOR_SUCCESS) {
- return MakeTestResultCode(uTestIndex, 2, uErr);;
+ return MakeTestResultCode(uTestIndex, 4, uErr);;
}
if(UsefulBuf_Compare(TestOutput, pTestCase->NotPreferred)) {
- return MakeTestResultCode(uTestIndex, 2, 200);
+ return MakeTestResultCode(uTestIndex, 5, 200);
}
- /* Number Encode of CDE */
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ /* Deterministic Encode */
QCBOREncode_Init(&EnCtx, TestOutBuffer);
QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_CDE);
QCBOREncode_AddDouble(&EnCtx, pTestCase->dNumber);
uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
if(uErr != QCBOR_SUCCESS) {
- return MakeTestResultCode(uTestIndex, 20, uErr);;
+ return MakeTestResultCode(uTestIndex, 6, uErr);;
}
if(UsefulBuf_Compare(TestOutput, pTestCase->CDE)) {
- return MakeTestResultCode(uTestIndex, 21, 200);
+ return MakeTestResultCode(uTestIndex, 7, 200);
}
- /* Number Encode of dCBOR */
+ /* dCBOR Encode */
QCBOREncode_Init(&EnCtx, TestOutBuffer);
QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_DCBOR);
QCBOREncode_AddDouble(&EnCtx, pTestCase->dNumber);
uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
if(uErr != QCBOR_SUCCESS) {
- return MakeTestResultCode(uTestIndex, 22, uErr);;
+ return MakeTestResultCode(uTestIndex, 8, uErr);;
}
if(UsefulBuf_Compare(TestOutput, pTestCase->DCBOR)) {
- return MakeTestResultCode(uTestIndex, 23, 200);
+ return MakeTestResultCode(uTestIndex, 9, 200);
}
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
- if(pTestCase->fNumber != 0) {
- QCBOREncode_Init(&EnCtx, TestOutBuffer);
- QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_DCBOR);
- QCBOREncode_AddFloat(&EnCtx, pTestCase->fNumber);
- uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
-
- if(uErr != QCBOR_SUCCESS) {
- return MakeTestResultCode(uTestIndex, 24, uErr);;
- }
- if(UsefulBuf_Compare(TestOutput, pTestCase->DCBOR)) {
- return MakeTestResultCode(uTestIndex, 25, 200);
- }
- }
-
-
- /* Number Decode of Preferred */
+ /* Decode Preferred */
QCBORDecode_Init(&DCtx, pTestCase->Preferred, 0);
uErr = QCBORDecode_GetNext(&DCtx, &Item);
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
if(uErr != QCBOR_SUCCESS) {
- return MakeTestResultCode(uTestIndex, 3, uErr);;
+ return MakeTestResultCode(uTestIndex, 10, uErr);
}
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
- return MakeTestResultCode(uTestIndex, 4, 0);
+ return MakeTestResultCode(uTestIndex, 11, 0);
}
if(isnan(pTestCase->dNumber)) {
if(!isnan(Item.val.dfnum)) {
- return MakeTestResultCode(uTestIndex, 5, 0);
+ return MakeTestResultCode(uTestIndex, 12, 0);
}
} else {
if(Item.val.dfnum != pTestCase->dNumber) {
- return MakeTestResultCode(uTestIndex, 6, 0);
+ return MakeTestResultCode(uTestIndex, 13, 0);
}
}
-#else /* QCBOR_DISABLE_FLOAT_HW_USE */
- /* When QCBOR_DISABLE_FLOAT_HW_USE is set, single-precision is not
- * converted to double when decoding, so test differently. len == 5
- * indicates single-precision in the encoded CBOR. */
- if(pTestCase->Preferred.len == 5) {
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+ if(pTestCase->Preferred.len == 3) {
+ if(uErr != QCBOR_ERR_PREFERRED_FLOAT_DISABLED) {
+ return MakeTestResultCode(uTestIndex, 14, uErr);
+ }
+ } else if(pTestCase->Preferred.len == 5) {
+ /* When QCBOR_DISABLE_PREFERRED_FLOAT is set, single-precision is not
+ * converted to double when decoding, so test differently. len == 5
+ * indicates single-precision in the encoded CBOR. */
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex, 15, uErr);
+ }
if(Item.uDataType != QCBOR_TYPE_FLOAT) {
- return MakeTestResultCode(uTestIndex, 41, 0);
+ return MakeTestResultCode(uTestIndex, 16, 0);
}
if(isnan(pTestCase->dNumber)) {
if(!isnan(Item.val.fnum)) {
- return MakeTestResultCode(uTestIndex, 51, 0);
+ return MakeTestResultCode(uTestIndex, 17, 0);
}
} else {
if(Item.val.fnum != pTestCase->fNumber) {
- return MakeTestResultCode(uTestIndex, 61, 0);
+ return MakeTestResultCode(uTestIndex, 18, 0);
}
}
} else {
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex, 19, uErr);
+ }
if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
- return MakeTestResultCode(uTestIndex, 42, 0);
+ return MakeTestResultCode(uTestIndex, 20, 0);
}
if(isnan(pTestCase->dNumber)) {
if(!isnan(Item.val.dfnum)) {
- return MakeTestResultCode(uTestIndex, 52, 0);
+ return MakeTestResultCode(uTestIndex, 21, 0);
}
} else {
if(Item.val.dfnum != pTestCase->dNumber) {
- return MakeTestResultCode(uTestIndex, 62, 0);
+ return MakeTestResultCode(uTestIndex, 22, 0);
}
}
}
-#endif /* ! QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
- /* Number Decode of Not Preferred */
+ /* Decode Not Preferred */
QCBORDecode_Init(&DCtx, pTestCase->NotPreferred, 0);
uErr = QCBORDecode_GetNext(&DCtx, &Item);
if(uErr != QCBOR_SUCCESS) {
- return MakeTestResultCode(uTestIndex, 7, uErr);;
+ return MakeTestResultCode(uTestIndex, 23, uErr);;
}
if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
- return MakeTestResultCode(uTestIndex, 8, 0);
+ return MakeTestResultCode(uTestIndex, 24, 0);
}
if(isnan(pTestCase->dNumber)) {
if(!isnan(Item.val.dfnum)) {
- return MakeTestResultCode(uTestIndex, 9, 0);
+ return MakeTestResultCode(uTestIndex, 25, 0);
}
} else {
if(Item.val.dfnum != pTestCase->dNumber) {
- return MakeTestResultCode(uTestIndex, 10, 0);
+ return MakeTestResultCode(uTestIndex, 26, 0);
}
}
-
- }
-
- /* Test a variety of NaNs with payloads */
- for(uTestIndex = 0; NaNTestCases[uTestIndex].Preferred.len != 0; uTestIndex++) {
- pNaNTestCase = &NaNTestCases[uTestIndex];
-
-
- if(uTestIndex == 4) {
- uErr = 99; /* For setting break points for particular tests */
- }
-
- /* NaN Encode of Preferred */
- QCBOREncode_Init(&EnCtx, TestOutBuffer);
- QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
- QCBOREncode_AddDouble(&EnCtx, UsefulBufUtil_CopyUint64ToDouble(pNaNTestCase->uDouble));
- uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
- if(uErr != QCBOR_SUCCESS) {
- return MakeTestResultCode(uTestIndex+100, 10, uErr);;
- }
- if(UsefulBuf_Compare(TestOutput, pNaNTestCase->Preferred)) {
- return MakeTestResultCode(uTestIndex+100, 10, 200);
- }
-
-#ifdef QCBOR_COMPARE_TO_HW_NAN_CONVERSION
- {
- /* This test is off by default. It's purpose is to check
- * QCBOR's mask-n-shift implementation against the HW/CPU
- * instructions that do conversion between double and single.
- * It is off because it is only used on occasion to verify
- * QCBOR and because it is suspected that some HW/CPU does
- * implement this correctly. NaN payloads are an obscure
- * feature. */
- float f;
- double d, d2;
-
- d = UsefulBufUtil_CopyUint64ToDouble(pNaNTestCase->uNumber);
-
- /* Cast the double to a single and then back to a double and
- * see if they are equal. If so, then the NaN payload doesn't
- * have any bits that are lost when converting to single and
- * it can be safely converted.
- *
- * This test can't be done for half-precision because it is
- * not widely supported.
- */
- f = (float)d;
- d2 = (double)f;
-
- /* The length of encoded doubles is 9, singles 5 and halves
- * 3. If there are NaN payload bits that can't be converted,
- * then the length must be 9.
- */
- if((uint64_t)d != (uint64_t)d2 && pNaNTestCase->Preferred.len != 9) {
- /* QCBOR conversion not the same as HW conversion */
- return MakeTestResultCode(uTestIndex, 9, 200);
- }
- }
-#endif /* QCBOR_COMPARE_TO_HW_NAN_CONVERSION */
-
-
- /* NaN Encode of Not Preferred */
- QCBOREncode_Init(&EnCtx, TestOutBuffer);
- QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
- QCBOREncode_AddDoubleNoPreferred(&EnCtx, UsefulBufUtil_CopyUint64ToDouble(pNaNTestCase->uDouble));
- uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
- if(uErr != QCBOR_SUCCESS) {
- return MakeTestResultCode(uTestIndex+100, 11, uErr);;
- }
- if(UsefulBuf_Compare(TestOutput, pNaNTestCase->NotPreferred)) {
- return MakeTestResultCode(uTestIndex+100, 11, 200);
- }
-
- /* NaN Decode of Not Preferred */
- QCBORDecode_Init(&DCtx, pNaNTestCase->Preferred, 0);
- QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
- uErr = QCBORDecode_GetNext(&DCtx, &Item);
- if(uErr != QCBOR_SUCCESS) {
- return MakeTestResultCode(uTestIndex+100, 12, uErr);
- }
-
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
-
- uDecoded = UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum);
- if(uDecoded != pNaNTestCase->uDouble) {
- return MakeTestResultCode(uTestIndex+100, 12, 200);
- }
-#else /* QCBOR_DISABLE_FLOAT_HW_USE */
- if(pNaNTestCase->Preferred.len == 5) {
- if(Item.uDataType != QCBOR_TYPE_FLOAT) {
- return MakeTestResultCode(uTestIndex, 4, 0);
- }
-
- uDecoded2 = UsefulBufUtil_CopyFloatToUint32(Item.val.fnum);
-
- if(uDecoded2 != pNaNTestCase->uSingle) {
- return MakeTestResultCode(uTestIndex, 4, 0);
- }
- } else {
- if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
- return MakeTestResultCode(uTestIndex, 4, 0);
- }
- uDecoded = UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum);
- if(uDecoded != pNaNTestCase->uDouble) {
- return MakeTestResultCode(uTestIndex+100, 12, 200);
- }
- }
-#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
-
- /* NaN Decode of Not Preferred */
- QCBORDecode_Init(&DCtx, pNaNTestCase->NotPreferred, 0);
- QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
- uErr = QCBORDecode_GetNext(&DCtx, &Item);
- if(uErr != QCBOR_SUCCESS) {
- return MakeTestResultCode(uTestIndex+100, 13, uErr);
- }
- uDecoded = UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum);
- if(uDecoded != pNaNTestCase->uDouble) {
- return MakeTestResultCode(uTestIndex+100, 13, 200);
- }
-
-
- /* NaN Encode of DCBOR */
- QCBOREncode_Init(&EnCtx, TestOutBuffer);
- QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_DCBOR | QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
- QCBOREncode_AddDouble(&EnCtx, UsefulBufUtil_CopyUint64ToDouble(pNaNTestCase->uDouble));
- uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
- if(uErr != QCBOR_SUCCESS) {
- return MakeTestResultCode(uTestIndex+100, 14, uErr);;
- }
- if(UsefulBuf_Compare(TestOutput, pNaNTestCase->DCBOR)) {
- return MakeTestResultCode(uTestIndex+100, 14, 200);
- }
-
}
return 0;
}
+/* Can't use the types double and float here because there's no compile
+ * time initializer in C to construct NaNs.
+
+ * The tests: encode the double in the 4 different ways and see the result is as expected
+ * encode the single in the 4 different ways and see the result is as expected
+ * decode the preferred and non-preferred (CDE is always the same as preferred; DCBOR is not reversable)
+ */
+struct NaNTestCase {
+ uint64_t uDouble; /* Converted to double in test */
+ uint32_t uSingle; /* Converted to single in test */
+ uint64_t uExpectedDouble;
+ uint32_t uExpectedSingle;
+ UsefulBufC Preferred;
+ UsefulBufC NotPreferred;
+ UsefulBufC CDE;
+ UsefulBufC DCBOR;
+};
+
+/* Always four lines per test case so shell scripts can process into
+ * other formats.
+ *
+ * C string literals are used because they are the shortest
+ * notation. They are used __with a length__ . Null termination
+ * doesn't work because there are bytes with value zero.
+ *
+ * While the CDE and dCBOR standards are not complete as of mid-2025,
+ * they are unlikely to change, so the tests here are likely correct.
+ */
+/* This assumes that the signficand of a float is made up of the qNaN bit and
+ * the payload. The qNaN bit is the most signficant. If not a qNaN, then it
+ * is an sNaN. For an sNaN not to be the floating point value, its significand
+ * must be non-zero. */
+static const struct NaNTestCase NaNTestCases[] = {
+ /* Reminder: DOUBLE_NAN_BITS | x00 is INFINITY, not a NaN */
+
+ /* double qNaN -- shortens to half */
+ {DOUBLE_NAN_BITS | DOUBLE_QNAN, 0,
+ DOUBLE_NAN_BITS | DOUBLE_QNAN, 0,
+ {"\xF9\x7E\x00", 3}, {"\xFB\x7F\xF8\x00\x00\x00\x00\x00\x00", 9},
+ {"\xF9\x7E\x00", 3}, {"\xF9\x7E\x00", 3}},
+
+ /* double negative qNaN -- shortens to half */
+ {DOUBLE_SIGN_MASK | DOUBLE_NAN_BITS | DOUBLE_QNAN, 0,
+ DOUBLE_SIGN_MASK| DOUBLE_NAN_BITS | DOUBLE_QNAN, 0,
+ {"\xF9\xFE\x00", 3}, {"\xFB\xFF\xF8\x00\x00\x00\x00\x00\x00", 9},
+ {"\xF9\xFE\x00", 3}, {"\xF9\x7E\x00", 3}},
+
+ /* double sNaN with payload of rightmost bit set -- no shorter encoding */
+ {DOUBLE_NAN_BITS | DOUBLE_SNAN | 0x01, 0,
+ DOUBLE_NAN_BITS | DOUBLE_SNAN | 0x01, 0,
+ {"\xFB\x7F\xF0\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\x7F\xF0\x00\x00\x00\x00\x00\x01", 9},
+ {"\xFB\x7F\xF0\x00\x00\x00\x00\x00\x01", 9}, {"\xF9\x7E\x00", 3}},
+
+ /* double negative sNaN with payload of rightmost bit set -- no shorter encoding */
+ {DOUBLE_SIGN_MASK | DOUBLE_NAN_BITS | DOUBLE_SNAN | 0x01, 0,
+ DOUBLE_SIGN_MASK | DOUBLE_NAN_BITS | DOUBLE_SNAN | 0x01, 0,
+ {"\xFB\xFF\xF0\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\xFF\xF0\x00\x00\x00\x00\x00\x01", 9},
+ {"\xFB\xFF\xF0\x00\x00\x00\x00\x00\x01", 9}, {"\xF9\x7E\x00", 3}},
+
+ /* double qNaN with 9 leftmost payload bits set -- shortens to half */
+ {DOUBLE_NAN_BITS | DOUBLE_QNAN | 0x7fc0000000000, 0,
+ DOUBLE_NAN_BITS | DOUBLE_QNAN | 0x7fc0000000000, 0,
+ {"\xF9\x7F\xFF", 3}, {"\xFB\x7F\xFF\xFC\x00\x00\x00\x00\x00", 9},
+ {"\xF9\x7F\xFF", 3}, {"\xF9\x7E\x00", 3}},
+
+ /* double sNaN with 10 rightmost payload bits set -- no shorter encoding */
+ {DOUBLE_NAN_BITS | DOUBLE_SNAN | 0x03ff, 0,
+ DOUBLE_NAN_BITS | DOUBLE_SNAN | 0x03ff, 0,
+ {"\xFB\x7F\xF0\x00\x00\x00\x00\x03\xFF", 9}, {"\xFB\x7F\xF0\x00\x00\x00\x00\x03\xFF", 9},
+ {"\xFB\x7F\xF0\x00\x00\x00\x00\x03\xFF", 9}, {"\xF9\x7E\x00", 3}},
+
+ /* double qNaN with 22 leftmost payload bits set -- shortens to single */
+ {DOUBLE_NAN_BITS | DOUBLE_QNAN | 0x7ffffe0000000, 0,
+ DOUBLE_NAN_BITS | DOUBLE_QNAN | 0x7ffffe0000000, SINGLE_NAN_BITS | 0x7fffff,
+ {"\xFA\x7F\xFF\xFF\xFF", 5}, {"\xFB\x7F\xFF\xFF\xFF\xE0\x00\x00\x00", 9},
+ {"\xFA\x7F\xFF\xFF\xFF", 5}, {"\xF9\x7E\x00", 3}},
+
+ /* double negative qNaN with 22 leftmost payload bits set -- shortens to single */
+ {DOUBLE_SIGN_MASK | DOUBLE_NAN_BITS | DOUBLE_QNAN | 0x7ffffe0000000, 0,
+ DOUBLE_SIGN_MASK | DOUBLE_NAN_BITS | DOUBLE_QNAN | 0x7ffffe0000000, SINGLE_SIGN_MASK | SINGLE_NAN_BITS | 0x7fffff,
+ {"\xFA\xFF\xFF\xFF\xFF", 5}, {"\xFB\xFF\xFF\xFF\xFF\xE0\x00\x00\x00", 9},
+ {"\xFA\xFF\xFF\xFF\xFF", 5}, {"\xF9\x7E\x00", 3}},
+
+ /* double sNaN with 23rd leftmost payload bit set -- shortens to single */
+ {DOUBLE_NAN_BITS | DOUBLE_SNAN | 0x0000020000000, 0,
+ DOUBLE_NAN_BITS | DOUBLE_SNAN | 0x0000020000000, SINGLE_NAN_BITS | 0x01,
+ {"\xFA\x7F\x80\x00\x01", 5}, {"\xFB\x7F\xF0\x00\x00\x20\x00\x00\x00", 9},
+ {"\xFA\x7F\x80\x00\x01", 5}, {"\xF9\x7E\x00", 3}},
+
+ /* double sNaN with randomly chosen bit pattern -- shortens to single */
+ {DOUBLE_NAN_BITS | DOUBLE_SNAN | 0x43d7c40000000, 0,
+ DOUBLE_NAN_BITS | DOUBLE_SNAN | 0x43d7c40000000, SINGLE_NAN_BITS | 0x21ebe2,
+ {"\xFA\x7F\xA1\xEB\xE2", 5}, {"\xFB\x7F\xF4\x3D\x7C\x40\x00\x00\x00", 9},
+ {"\xFA\x7F\xA1\xEB\xE2", 5}, {"\xF9\x7E\x00", 3}},
+
+ /* double sNaN with 23 leftmost payload bits set -- no shorter encoding */
+ {DOUBLE_NAN_BITS | DOUBLE_SNAN | 0x7fffff0000000, 0,
+ DOUBLE_NAN_BITS | DOUBLE_SNAN | 0x7fffff0000000, 0,
+ {"\xFB\x7F\xF7\xFF\xFF\xF0\x00\x00\x00", 9}, {"\xFB\x7F\xF7\xFF\xFF\xF0\x00\x00\x00", 9},
+ {"\xFB\x7F\xF7\xFF\xFF\xF0\x00\x00\x00", 9}, {"\xF9\x7E\x00", 3}},
+
+ /* double qNaN with all bits set -- no shorter encoding */
+ {DOUBLE_NAN_BITS | DOUBLE_QNAN | 0x7ffffffffffff, 0,
+ DOUBLE_NAN_BITS | DOUBLE_QNAN | 0x7ffffffffffff, 0,
+ {"\xFB\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9},
+ {"\xFB\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xF9\x7E\x00", 3}},
+
+ /* single qNaN with payload 0x00 -- shortens to half */
+ {0, SINGLE_NAN_BITS | SINGLE_QNAN,
+ DOUBLE_NAN_BITS | DOUBLE_QNAN, 0,
+ {"\xF9\x7E\x00", 3}, {"\xFA\x7F\xC0\x00\x00", 5},
+ {"\xF9\x7E\x00", 3}, {"\xF9\x7E\x00", 3}},
+
+ /* sNan with payload 0x00 is not a NaN, it's infinity */
+
+ /* single sNan with payload 0x01 -- no shorter encoding */
+ {0, SINGLE_NAN_BITS | SINGLE_SNAN | 0x01,
+ DOUBLE_NAN_BITS | (0x01 << 29), 0,
+ {"\xFA\x7F\x80\x00\x01", 5}, {"\xFA\x7F\x80\x00\x01", 5},
+ {"\xFA\x7F\x80\x00\x01", 5}, {"\xF9\x7E\x00", 3}},
+
+ /* single qNaN with 9 bit payload -- shortens to half */
+ {0, SINGLE_NAN_BITS | SINGLE_QNAN | 0x3fe000,
+ DOUBLE_NAN_BITS | ((SINGLE_QNAN | 0x3fe000ULL) << 29), 0,
+ {"\xF9\x7F\xFF", 3}, {"\xFA\x7F\xFF\xE0\x00", 5},
+ {"\xF9\x7F\xFF", 3}, {"\xF9\x7E\x00", 3}},
+
+ /* single qNaN with 10 bit payload -- no shorter encoding */
+ {0, SINGLE_NAN_BITS | SINGLE_QNAN | 0x3ff000,
+ DOUBLE_NAN_BITS | ((SINGLE_QNAN | 0x3ff000ULL) << 29), 0,
+ {"\xFA\x7F\xFF\xF0\x00", 5}, {"\xFA\x7F\xFF\xF0\x00", 5},
+ {"\xFA\x7F\xFF\xF0\x00", 5}, {"\xF9\x7E\x00", 3}},
+
+ /* single sNaN with 9 bit payload -- shortens to half */
+ {0, SINGLE_NAN_BITS | SINGLE_SNAN | 0x3fe000,
+ DOUBLE_NAN_BITS | ((SINGLE_SNAN | 0x3fe000ULL) << 29), 0,
+ {"\xF9\x7D\xFF", 3}, {"\xFA\x7F\xBF\xE0\x00", 5},
+ {"\xF9\x7D\xFF", 3}, {"\xF9\x7E\x00", 3}},
+
+ /* single sNaN with 10 bit payload -- no shorter encoding */
+ {0, SINGLE_NAN_BITS | SINGLE_SNAN | 0x3ff000,
+ DOUBLE_NAN_BITS | ((SINGLE_SNAN | 0x3ff000ULL) << 29), 0,
+ {"\xFA\x7F\xBF\xF0\x00", 5}, {"\xFA\x7F\xBF\xF0\x00", 5},
+ {"\xFA\x7F\xBF\xF0\x00", 5}, {"\xF9\x7E\x00", 3}},
+
+ /* List terminator */
+ {0, 0, 0, 0, {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0} }
+};
+
/* Public function. See float_tests.h */
-int32_t
+int32_t
+NaNPayloadsTest(void)
+{
+ const struct NaNTestCase *pNaNTestCase;
+ unsigned int uTestIndex;
+ QCBORError uErr;
+ QCBOREncodeContext EnCtx;
+ MakeUsefulBufOnStack( TestOutBuffer, 20);
+ UsefulBufC TestOutput;
+ QCBORDecodeContext DCtx;
+ QCBORItem Item;
+ uint64_t uDecoded;
+
+ /* Test a variety of NaNs with payloads */
+ for(uTestIndex = 0; NaNTestCases[uTestIndex].Preferred.len != 0; uTestIndex++) {
+ pNaNTestCase = &NaNTestCases[uTestIndex];
+
+ if(uTestIndex == 7) {
+ uErr = 99; /* For setting break points for a particular test */
+ }
+
+ if(pNaNTestCase->uDouble) {
+ /* NaN Encode of Preferred */
+ QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
+ QCBOREncode_AddDouble(&EnCtx, UsefulBufUtil_CopyUint64ToDouble(pNaNTestCase->uDouble));
+ uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex+100, 10, uErr);;
+ }
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ if(UsefulBuf_Compare(TestOutput, pNaNTestCase->Preferred)) {
+ return MakeTestResultCode(uTestIndex+100, 11, 200);
+ }
+ if(CompareToCarsten(pNaNTestCase->uDouble, TestOutput, pNaNTestCase->Preferred)) {
+ return MakeTestResultCode(uTestIndex+100, 12, 200);
+ }
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+ if(UsefulBuf_Compare(TestOutput, pNaNTestCase->NotPreferred)) {
+ return MakeTestResultCode(uTestIndex+100, 122, 200);
+ }
+#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
+
+ if(HWCheckFloatToDouble(pNaNTestCase->uDouble, pNaNTestCase->uSingle)) {
+ return MakeTestResultCode(uTestIndex+100, 121, 200);
+ }
+
+ /* NaN Encode of Not Preferred */
+ QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
+ QCBOREncode_AddDoubleNoPreferred(&EnCtx, UsefulBufUtil_CopyUint64ToDouble(pNaNTestCase->uDouble));
+ uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex+100, 13, uErr);;
+ }
+ if(UsefulBuf_Compare(TestOutput, pNaNTestCase->NotPreferred)) {
+ return MakeTestResultCode(uTestIndex+100, 14, 200);
+ }
+
+ /* NaN Decode of Preferred */
+ QCBORDecode_Init(&DCtx, pNaNTestCase->Preferred, 0);
+ QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
+ uErr = QCBORDecode_GetNext(&DCtx, &Item);
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex+100, 15, uErr);
+ }
+ uDecoded = UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum);
+ if(uDecoded != pNaNTestCase->uDouble) {
+ return MakeTestResultCode(uTestIndex+100, 11, 200);
+ }
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+ if(pNaNTestCase->Preferred.len == 9) {
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex+100, 17, uErr);
+ }
+
+ uDecoded = UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum);
+ if(uDecoded != pNaNTestCase->uDouble) {
+ return MakeTestResultCode(uTestIndex+100, 18, 200);
+ }
+ } else if(pNaNTestCase->Preferred.len == 5) {
+ if(Item.uDataType != QCBOR_TYPE_FLOAT) {
+ return MakeTestResultCode(uTestIndex, 19, 0);
+ }
+
+ uint32_t uDecoded2x = UsefulBufUtil_CopyFloatToUint32(Item.val.fnum);
+
+ if(uDecoded2x != pNaNTestCase->uExpectedSingle) {
+ return MakeTestResultCode(uTestIndex, 20, 0);
+ }
+ } else {
+ /* Serialized to half precision */
+ if(Item.uDataType != QCBOR_TYPE_NONE) {
+ return MakeTestResultCode(uTestIndex, 21, 0);
+ }
+ }
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+ /* NaN Decode of Not Preferred */
+ QCBORDecode_Init(&DCtx, pNaNTestCase->NotPreferred, 0);
+ QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
+ uErr = QCBORDecode_GetNext(&DCtx, &Item);
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex+100, 22, uErr);
+ }
+ uDecoded = UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum);
+ if(uDecoded != pNaNTestCase->uDouble) {
+ return MakeTestResultCode(uTestIndex+100, 23, 200);
+ }
+
+ /* NaN Encode of CDE */
+ QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_CDE| QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
+ QCBOREncode_AddDouble(&EnCtx, UsefulBufUtil_CopyUint64ToDouble(pNaNTestCase->uDouble));
+ uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex+100, 24, uErr);;
+ }
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ if(UsefulBuf_Compare(TestOutput, pNaNTestCase->Preferred)) {
+ return MakeTestResultCode(uTestIndex+100, 241, 200);
+ }
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+ if(UsefulBuf_Compare(TestOutput, pNaNTestCase->NotPreferred)) {
+ return MakeTestResultCode(uTestIndex+100, 25, 200);
+ }
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+ /* NaN Encode of DCBOR */
+ QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_DCBOR | QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
+ QCBOREncode_AddDouble(&EnCtx, UsefulBufUtil_CopyUint64ToDouble(pNaNTestCase->uDouble));
+ uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex+100, 26, uErr);;
+ }
+ if(UsefulBuf_Compare(TestOutput, pNaNTestCase->DCBOR)) {
+ return MakeTestResultCode(uTestIndex+100, 27, 200);
+ }
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+ if(uErr != QCBOR_ERR_PREFERRED_FLOAT_DISABLED) {
+ return MakeTestResultCode(uTestIndex+100, 28, uErr);
+ }
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+ } else {
+ /* --- uSingle tests ---- */
+ /* NaN Encode of Preferred */
+ QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
+ QCBOREncode_AddFloat(&EnCtx, UsefulBufUtil_CopyUint32ToFloat(pNaNTestCase->uSingle));
+ uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex+100, 29, uErr);;
+ }
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ if(UsefulBuf_Compare(TestOutput, pNaNTestCase->Preferred)) {
+ return MakeTestResultCode(uTestIndex+100, 30, 200);
+ }
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+ if(UsefulBuf_Compare(TestOutput, pNaNTestCase->NotPreferred)) {
+ return MakeTestResultCode(uTestIndex+100, 31, 200);
+ }
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+ /* NaN Encode of Not Preferred */
+ QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
+ QCBOREncode_AddFloatNoPreferred(&EnCtx, UsefulBufUtil_CopyUint32ToFloat(pNaNTestCase->uSingle));
+ uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex+100, 32, uErr);;
+ }
+ if(UsefulBuf_Compare(TestOutput, pNaNTestCase->NotPreferred)) {
+ return MakeTestResultCode(uTestIndex+100, 33, 200);
+ }
+
+ /* NaN Decode of Preferred */
+ QCBORDecode_Init(&DCtx, pNaNTestCase->Preferred, 0);
+ QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
+ uErr = QCBORDecode_GetNext(&DCtx, &Item);
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex+100, 34, uErr);
+ }
+ uDecoded = UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum);
+ if(uDecoded != pNaNTestCase->uExpectedDouble) {
+ return MakeTestResultCode(uTestIndex+100, 35, 200);
+ }
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+ if(pNaNTestCase->Preferred.len == 5) {
+ uint32_t uDecoded2x = UsefulBufUtil_CopyFloatToUint32(Item.val.fnum);
+ if(uDecoded2x != pNaNTestCase->uSingle) {
+ return MakeTestResultCode(uTestIndex+100, 36, 200);
+ }
+ } else {
+ if(uErr != QCBOR_ERR_PREFERRED_FLOAT_DISABLED) {
+ return MakeTestResultCode(uTestIndex+100, 37, 200);
+ }
+ }
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+ /* NaN Decode of Not Preferred */
+ QCBORDecode_Init(&DCtx, pNaNTestCase->NotPreferred, 0);
+ QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
+ uErr = QCBORDecode_GetNext(&DCtx, &Item);
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex+100, 38, uErr);
+ }
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ uDecoded = UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum);
+ if(uDecoded != pNaNTestCase->uExpectedDouble) {
+ return MakeTestResultCode(uTestIndex+100, 39, 200);
+ }
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+ if(pNaNTestCase->NotPreferred.len == 5) {
+ uint32_t uDecoded22 = UsefulBufUtil_CopyFloatToUint32(Item.val.fnum);
+ if(uDecoded22 != pNaNTestCase->uSingle) {
+ return MakeTestResultCode(uTestIndex+100, 40, 200);
+ }
+ }
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+ if(HWCheckDoubleToFloat(pNaNTestCase->uSingle, pNaNTestCase->uExpectedDouble)) {
+ return MakeTestResultCode(uTestIndex+100, 401, 200);
+ }
+
+ /* NaN Encode of CDE */
+ QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_CDE| QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
+ QCBOREncode_AddFloat(&EnCtx, UsefulBufUtil_CopyUint32ToFloat(pNaNTestCase->uSingle));
+ uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex+100, 41, uErr);;
+ }
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ if(UsefulBuf_Compare(TestOutput, pNaNTestCase->CDE)) {
+ return MakeTestResultCode(uTestIndex+100, 42, 200);
+ }
+#else /* ! #ifndef QCBOR_DISABLE_PREFERRED_FLOAT */
+ if(UsefulBuf_Compare(TestOutput, pNaNTestCase->NotPreferred)) {
+ return MakeTestResultCode(uTestIndex+100, 43, 200);
+ }
+#endif /* ! #ifndef QCBOR_DISABLE_PREFERRED_FLOAT */
+
+ /* NaN Encode of DCBOR */
+ QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_DCBOR | QCBOR_ENCODE_CONFIG_ALLOW_NAN_PAYLOAD);
+ QCBOREncode_AddFloat(&EnCtx, UsefulBufUtil_CopyUint32ToFloat(pNaNTestCase->uSingle));
+ uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(uTestIndex+100, 44, uErr);;
+ }
+ if(UsefulBuf_Compare(TestOutput, pNaNTestCase->DCBOR)) {
+ return MakeTestResultCode(uTestIndex+100, 45, 200);
+ }
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+ if(uErr != QCBOR_ERR_PREFERRED_FLOAT_DISABLED) {
+ return MakeTestResultCode(uTestIndex+100, 46, uErr);
+ }
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+ }
+ }
+
+ /* Special one-off for 100% coverage */
+ QCBOREncode_Init(&EnCtx, TestOutBuffer);
+ QCBOREncode_Config(&EnCtx, QCBOR_ENCODE_CONFIG_DCBOR);
+ QCBOREncode_AddFloat(&EnCtx, 0);
+ uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
+ if(uErr != QCBOR_SUCCESS) {
+ return MakeTestResultCode(199, 100, uErr);;
+ }
+ if(UsefulBuf_Compare(TestOutput, UsefulBuf_FROM_SZ_LITERAL("\x00"))) {
+ return MakeTestResultCode(199, 101, 200);
+ }
+#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+ if(uErr != QCBOR_ERR_PREFERRED_FLOAT_DISABLED) {
+ return MakeTestResultCode(uTestIndex+100, 261, uErr);
+ }
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+ return 0;
+}
+
+
+/* Public function. See float_tests.h */
+int32_t
HalfPrecisionAgainstRFCCodeTest(void)
{
QCBORItem Item;
@@ -753,7 +1259,7 @@
return 0;
}
-#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
/*
@@ -766,10 +1272,10 @@
* NaN, // Double
* Infinity, // Double
* 0.0, // Half (Duplicate because of use in encode tests)
- * 3.140000104904175, // Single
- * 0.0, // Single
- * NaN, // Single
- * Infinity, // Single
+ * 3.140000104904175, // Single XXX
+ * 0.0, // Single XXX
+ * NaN, // Single XXX
+ * Infinity, // Single XXX
* {100: 0.0, 101: 3.1415926, "euler": 2.718281828459045, 105: 0.0,
* 102: 0.0, 103: 3.141592502593994, "euler2": 2.7182817459106445, 106: 0.0}]
*/
@@ -840,7 +1346,7 @@
GeneralFloatEncodeTests(void)
{
/* See FloatNumberTests() for tests that really cover lots of float values.
- * Add new tests for new values or decode modes there.
+ * Add new tests for new values or decode modes there.
* This test is primarily to cover all the float encode methods. */
UsefulBufC Encoded;
@@ -904,15 +1410,15 @@
/* Public function. See float_tests.h */
-int32_t
+int32_t
GeneralFloatDecodeTests(void)
{
- /* See FloatNumberTests() for tests that really cover lots of float values */
+ /* See FloatNumberTests() for tests that really covers the float values.
+ * This is retained to cover GetDouble() and decode of a single 0 */
QCBORItem Item;
QCBORError uErr;
QCBORDecodeContext DC;
-
UsefulBufC TestData = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloats);
QCBORDecode_Init(&DC, TestData, 0);
@@ -923,7 +1429,7 @@
/* 0.0 half-precision */
uErr = QCBORDecode_GetNext(&DC, &Item);
- if(uErr != FLOAT_ERR_CODE_NO_HALF_PREC(QCBOR_SUCCESS)
+ if(uErr != FLOAT_ERR_CODE_NO_PREF_FLOAT(QCBOR_SUCCESS)
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
|| Item.uDataType != QCBOR_TYPE_DOUBLE
|| Item.val.dfnum != 0.0
@@ -988,7 +1494,7 @@
/* 0.0 half-precision (again) */
uErr = QCBORDecode_GetNext(&DC, &Item);
- if(uErr != FLOAT_ERR_CODE_NO_HALF_PREC(QCBOR_SUCCESS)
+ if(uErr != FLOAT_ERR_CODE_NO_PREF_FLOAT(QCBOR_SUCCESS)
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
|| Item.uDataType != QCBOR_TYPE_DOUBLE
|| Item.val.dfnum != 0.0
@@ -1003,13 +1509,13 @@
uErr = QCBORDecode_GetNext(&DC, &Item);
if(uErr != FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS)
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
|| Item.uDataType != QCBOR_TYPE_DOUBLE
|| 3.1400001049041748 != Item.val.dfnum
#else /* QCBOR_DISABLE_FLOAT_HW_USE */
|| Item.uDataType != QCBOR_TYPE_FLOAT
|| 3.140000f != Item.val.fnum
-#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
#else /* USEFULBUF_DISABLE_ALL_FLOAT */
|| Item.uDataType != QCBOR_TYPE_NONE
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
@@ -1021,13 +1527,13 @@
uErr = QCBORDecode_GetNext(&DC, &Item);
if(uErr != FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS)
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
|| Item.uDataType != QCBOR_TYPE_DOUBLE
|| Item.val.dfnum != 0.0
#else /* QCBOR_DISABLE_FLOAT_HW_USE */
|| Item.uDataType != QCBOR_TYPE_FLOAT
|| Item.val.fnum != 0.0f
-#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
#else /* USEFULBUF_DISABLE_ALL_FLOAT */
|| Item.uDataType != QCBOR_TYPE_NONE
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
@@ -1039,13 +1545,13 @@
uErr = QCBORDecode_GetNext(&DC, &Item);
if(uErr != FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS)
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
|| Item.uDataType != QCBOR_TYPE_DOUBLE
|| !isnan(Item.val.dfnum)
-#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+#else /* QCBOR_DISABLE_PREFERRED_FLOAT */
|| Item.uDataType != QCBOR_TYPE_FLOAT
|| !isnan(Item.val.fnum)
-#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
#else /* USEFULBUF_DISABLE_ALL_FLOAT */
|| Item.uDataType != QCBOR_TYPE_NONE
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
@@ -1057,13 +1563,13 @@
uErr = QCBORDecode_GetNext(&DC, &Item);
if(uErr != FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS)
#ifndef USEFULBUF_DISABLE_ALL_FLOAT
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
|| Item.uDataType != QCBOR_TYPE_DOUBLE
|| Item.val.dfnum != INFINITY
-#else /* QCBOR_DISABLE_FLOAT_HW_USE */
+#else /* QCBOR_DISABLE_PREFERRED_FLOAT */
|| Item.uDataType != QCBOR_TYPE_FLOAT
|| Item.val.fnum != INFINITY
-#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
#else /* USEFULBUF_DISABLE_ALL_FLOAT */
|| Item.uDataType != QCBOR_TYPE_NONE
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
@@ -1083,7 +1589,7 @@
/* 0.0 half-precision */
QCBORDecode_GetDouble(&DC, &d);
uErr = QCBORDecode_GetAndResetError(&DC);
- if(uErr != FLOAT_ERR_CODE_NO_HALF_PREC(QCBOR_SUCCESS)
+ if(uErr != FLOAT_ERR_CODE_NO_PREF_FLOAT(QCBOR_SUCCESS)
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
|| d != 0.0
#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
@@ -1122,7 +1628,7 @@
/* 0.0 half-precision */
QCBORDecode_GetDouble(&DC, &d);
uErr = QCBORDecode_GetAndResetError(&DC);
- if(uErr != FLOAT_ERR_CODE_NO_HALF_PREC(QCBOR_SUCCESS)
+ if(uErr != FLOAT_ERR_CODE_NO_PREF_FLOAT(QCBOR_SUCCESS)
#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
|| d != 0.0
#endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
@@ -1133,10 +1639,10 @@
/* 3.140000104904175 single-precision */
QCBORDecode_GetDouble(&DC, &d);
uErr = QCBORDecode_GetAndResetError(&DC);
- if(uErr != FLOAT_ERR_CODE_NO_FLOAT_HW(QCBOR_SUCCESS)
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+ if(uErr != FLOAT_ERR_CODE_NO_PREF_FLOAT(QCBOR_SUCCESS) /* Different in 2.0 and 1.6 */
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
|| d != 3.140000104904175
-#endif
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
) {
return MakeTestResultCode(1, 7, uErr);
}
@@ -1144,20 +1650,20 @@
/* 0.0 single-precision */
QCBORDecode_GetDouble(&DC, &d);
uErr = QCBORDecode_GetAndResetError(&DC);
- if(uErr != FLOAT_ERR_CODE_NO_FLOAT_HW(QCBOR_SUCCESS)
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+ if(uErr != FLOAT_ERR_CODE_NO_PREF_FLOAT(QCBOR_SUCCESS) /* Different in 2.0 and 1.6 */
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
|| d != 0.0
-#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
) {
return MakeTestResultCode(1, 8, uErr);
}
/* NaN single-precision */
QCBORDecode_GetDouble(&DC, &d);
- if(uErr != FLOAT_ERR_CODE_NO_FLOAT_HW(QCBOR_SUCCESS)
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+ if(uErr != FLOAT_ERR_CODE_NO_PREF_FLOAT(QCBOR_SUCCESS) /* Different in 2.0 and 1.6 */
+#ifndef QCBOR_DISABLE_PREFERRED_FLOAT
|| !isnan(d)
-#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
) {
return MakeTestResultCode(1, 9, uErr);
}
@@ -1165,60 +1671,15 @@
/* Infinity single-precision */
QCBORDecode_GetDouble(&DC, &d);
uErr = QCBORDecode_GetAndResetError(&DC);
- if(uErr != FLOAT_ERR_CODE_NO_FLOAT_HW(QCBOR_SUCCESS)
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+ if(uErr != FLOAT_ERR_CODE_NO_PREF_FLOAT(QCBOR_SUCCESS) /* Different in 2.0 and 1.6 */
+#ifndef QCBOR_DISABLQCBOR_DISABLE_PREFERRED_FLOATE_FLOAT_HW_USE
|| d != INFINITY
-#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* ! QCBOR_DISABLE_PREFERRED_FLOAT */
) {
return MakeTestResultCode(1, 10, uErr);
}
#endif /* USEFULBUF_DISABLE_ALL_FLOAT */
-
return 0;
}
-
-
-#ifdef NAN_EXPERIMENT
-/*
- Code for checking what the double to float cast does with
- NaNs. Not run as part of tests. Keep it around to
- be able to check various platforms and CPUs.
- */
-
-#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
-#define DOUBLE_NUM_EXPONENT_BITS (11)
-#define DOUBLE_NUM_SIGN_BITS (1)
-
-#define DOUBLE_SIGNIFICAND_SHIFT (0)
-#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
-#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
-
-#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
-#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
-#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
-#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
-
-
-static int NaNExperiments() {
- double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
- double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
- double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
-
- float f1 = (float)dqNaN;
- float f2 = (float)dsNaN;
- float f3 = (float)dqNaNPayload;
-
-
- uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
- uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
- uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
-
- // Result of this on x86 is that every NaN is a qNaN. The intel
- // CVTSD2SS instruction ignores the NaN payload and even converts
- // a sNaN to a qNaN.
-
- return 0;
-}
-#endif /* NAN_EXPERIMENT */