Fix preferred serialization of subnormals (#192)

Preferred Serialization now fully supports conversion to/from half, single and double subnormals. This includes NaN payloads.

The tests for floating-point are much better organized and give greater coverage. 

IEEE 754 code is better organized and cleaner.



* Fix preferred serialization of subnormals (checkpoint)

* Check point progress

* Preferred float mostly working and tests passing

* added NaN tests

* Fix up ieee754.h; a few other compiler warnings

* decoding NaN payloads fix; rework half-double; tests

* Code tidyness

* indent to 3, not 4

* TODO's are done in other test; code tidy

* test running with float HW use disabled

* Remove / rearrange float tests

* Fix full float ifdef test fan out

* Code tidiness; sort out final TODO's

---------

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/test/float_tests.c b/test/float_tests.c
index 2bf5fad..1a7ade1 100644
--- a/test/float_tests.c
+++ b/test/float_tests.c
@@ -1,32 +1,33 @@
-/*==============================================================================
- float_tests.c -- tests for float and conversion to/from half-precision
-
- Copyright (c) 2018-2020, Laurence Lundblade. All rights reserved.
- Copyright (c) 2021, Arm Limited. All rights reserved.
-
- SPDX-License-Identifier: BSD-3-Clause
-
- See BSD-3-Clause license in README.md
-
- Created on 9/19/18
- =============================================================================*/
+/* ==========================================================================
+ * float_tests.c -- tests for float and conversion to/from half-precision
+ *
+ * Copyright (c) 2018-2024, Laurence Lundblade. All rights reserved.
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ *
+ * Created on 9/19/18
+ * ========================================================================= */
 
 
 #include "float_tests.h"
 #include "qcbor/qcbor_encode.h"
 #include "qcbor/qcbor_decode.h"
 #include "qcbor/qcbor_spiffy_decode.h"
-#include <math.h> // For INFINITY and NAN and isnan()
+#include <math.h> /* For INFINITY and NAN and isnan() */
 
 
-/* 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 zz is
- * check being performed
+
+/* 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
+ * zz is check being performed
  */
-static inline int32_t MakeTestResultCode(uint32_t   uTestCase,
-                                         uint32_t   uTestNumber,
-                                         QCBORError uErrorCode)
+static inline int32_t
+MakeTestResultCode(uint32_t   uTestCase,
+                   uint32_t   uTestNumber,
+                   QCBORError uErrorCode)
 {
    uint32_t uCode = (uTestCase * 1000000) +
                     (uTestNumber * 1000) +
@@ -40,585 +41,567 @@
 #include "half_to_double_from_rfc7049.h"
 
 
-/*
- Half-precision values that are input to test half-precision decoding
+struct DoubleTestCase {
+   double      dNumber;
+   double      fNumber;
+   UsefulBufC  Preferred;
+   UsefulBufC  NotPreferred;
+   UsefulBufC  CDE;
+   UsefulBufC  DCBOR;
+};
 
- As decoded by http://cbor.me
- {"zero": 0.0,
- "infinitity": Infinity,
- "negative infinitity": -Infinity,
- "NaN": NaN,
- "one": 1.0,
- "one third": 0.333251953125,
- "largest half-precision": 65504.0,
- "too-large half-precision": Infinity,
- "smallest subnormal": 5.960464477539063e-8,
- "smallest normal": 0.00006097555160522461,
- "biggest subnormal": 0.00006103515625,
- "subnormal single": 0.0,
- 3: -2.0,
- 4: NaN,
- 5: NaN,
- 6: NaN,
- 7: NaN}
+/* Boundaries for all destination conversions to test at.
+ *
+ * smallest subnormal single  1.401298464324817e-45   2^^-149
+ * largest subnormal single   1.1754942106924411e-38  2^^-126
+ * smallest normal single     1.1754943508222875e-38
+ * largest single             3.4028234663852886E+38
+ *
+ * smallest subnormal half   5.9604644775390625E-8
+ * largest subnormal half    6.097555160522461E-5
+ * smallest normal half      6.103515625E-5
+ * largest half              65504.0
+ *
+ * Boundaries for origin conversions
+ * smallest subnormal double 5.0e-324  2^^-1074
+ * largest subnormal double
+ * smallest normal double 2.2250738585072014e-308  2^^-1022
+ * largest normal double 1.7976931348623157e308 2^^-1023
  */
-static const uint8_t spExpectedHalf[] = {
-    0xB1,
-        0x64,
-            0x7A, 0x65, 0x72, 0x6F,
-        0xF9, 0x00, 0x00, // half-precision 0.000
-        0x6A,
-            0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
-        0xF9, 0x7C, 0x00, // Infinity
-        0x73,
-            0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E,
-            0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
-        0xF9, 0xFC, 0x00, // -Inifinity
-        0x63,
-            0x4E, 0x61, 0x4E,
-        0xF9, 0x7E, 0x00, // NaN
-        0x63,
-            0x6F, 0x6E, 0x65,
-        0xF9, 0x3C, 0x00, // 1.0
-        0x69,
-            0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
-        0xF9, 0x35, 0x55, // half-precsion one third 0.333251953125
-        0x76,
-            0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C,
-            0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
-        0xF9, 0x7B, 0xFF, // largest half-precision 65504.0
-        0x78, 0x18,
-            0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x68,
-            0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69,
-            0x6F, 0x6E,
-        0xF9, 0x7C, 0x00, // Infinity
-        0x72,
-            0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
-            0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
-        0xF9, 0x00, 0x01, // Smallest half-precision subnormal 0.000000059604645
-        0x71,
-            0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62,
-            0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
-        0xF9, 0x03, 0xFF, // Largest half-precision subnormal 0.0000609755516
-        0x6F,
-            0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
-            0x72, 0x6D, 0x61, 0x6C,
-        0xF9, 0x04, 0x00,  // Smallest half-precision normal 0.000061988
-        0x70,
-            0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73,
-            0x69, 0x6E, 0x67, 0x6C, 0x65,
-        0xF9, 0x00, 0x00,
-        0x03,
-        0xF9, 0xC0, 0x00,    // -2
-        0x04,
-        0xF9, 0x7E, 0x00,    // qNaN
-        0x05,
-        0xF9, 0x7C, 0x01,    // sNaN
-        0x06,
-        0xF9, 0x7E, 0x0F,    // qNaN with payload 0x0f
-        0x07,
-        0xF9, 0x7C, 0x0F,    // sNaN with payload 0x0f
+
+/* 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 DoubleTestCase DoubleTestCases[] =  {
+   /* Zero */
+   {0.0,                                         0.0f,
+    {"\xF9\x00\x00", 3},                         {"\xFB\x00\x00\x00\x00\x00\x00\x00\x00", 9},
+    {"\xF9\x00\x00", 3},                         {"\xF9\x00\x00", 3}},
+
+   /* Negative Zero */
+   {-0.0,                                        -0.0f,
+    {"\xF9\x80\x00", 3},                         {"\xFB\x80\x00\x00\x00\x00\x00\x00\x00", 9},
+    {"\xF9\x80\x00", 3},                         {"\xF9\x80\x00", 3}},
+
+   /* NaN */
+   {NAN,                                         NAN,
+    {"\xF9\x7E\x00", 3},                         {"\xFB\x7F\xF8\x00\x00\x00\x00\x00\x00", 9},
+    {"\xF9\x7E\x00", 3},                         {"\xF9\x7E\x00", 3}},
+
+   /* Infinity */
+   {INFINITY,                                    INFINITY,
+    {"\xF9\x7C\x00", 3},                         {"\xFB\x7F\xF0\x00\x00\x00\x00\x00\x00", 9},
+    {"\xF9\x7C\x00", 3},                         {"\xF9\x7C\x00", 3}},
+
+   /* Negative Infinity */
+   {-INFINITY,                                   -INFINITY,
+    {"\xF9\xFC\x00", 3},                         {"\xFB\xFF\xF0\x00\x00\x00\x00\x00\x00", 9},
+    {"\xF9\xFC\x00", 3},                         {"\xF9\xFC\x00", 3}},
+
+   /* 1.0 */
+   {1.0,                                         1.0f,
+    {"\xF9\x3C\x00", 3},                         {"\xFB\x3F\xF0\x00\x00\x00\x00\x00\x00", 9},
+    {"\xF9\x3C\x00", 3},                         {"\xF9\x3C\x00", 3}},
+
+   /* -2.0 -- a negative number that is not zero */
+   {-2.0,                                        -2.0f,
+    {"\xF9\xC0\x00", 3},                         {"\xFB\xC0\x00\x00\x00\x00\x00\x00\x00", 9},
+    {"\xF9\xC0\x00", 3},                         {"\xF9\x3C\x00", 3}},
+
+   /* 1/3 */
+   {0.333251953125,                              0.333251953125f,
+    {"\xF9\x35\x55", 3},                         {"\xFB\x3F\xD5\x54\x00\x00\x00\x00\x00", 9},
+    {"\xF9\x35\x55", 3},                         {"\xF9\x35\x55", 3}},
+
+   /* 5.9604644775390625E-8 -- smallest half-precision subnormal */
+   {5.9604644775390625E-8,                       0.0f,
+    {"\xF9\x00\x01", 3},                         {"\xFB\x3E\x70\x00\x00\x00\x00\x00\x00", 9},
+    {"\xF9\x00\x01", 3},                         {"\xF9\x00\x01", 3}},
+
+   /* 3.0517578125E-5 -- a half-precision subnormal */
+   {3.0517578125E-5,                             0.0f,
+    {"\xF9\x02\x00", 3},                         {"\xFB\x3F\x00\x00\x00\x00\x00\x00\x00", 9},
+    {"\xF9\x02\x00", 3},                         {"\xF9\x02\x00", 3}},
+
+   /* 6.097555160522461E-5 -- largest half-precision subnormal */
+   {6.097555160522461E-5,                        0.0f,
+    {"\xF9\x03\xFF", 3},                         {"\xFB\x3F\x0F\xF8\x00\x00\x00\x00\x00", 9},
+    {"\xF9\x03\xFF", 3},                         {"\xF9\04\00", 3}},
+
+   /* 6.103515625E-5 -- smallest possible half-precision normal */
+   {6.103515625E-5,                              0.0f,
+    {"\xF9\04\00", 3},                           {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x00", 9},
+    {"\xF9\04\00", 3},                           {"\xF9\04\00", 3}},
+
+   /* 6.1035156250000014E-5 -- slightly larger than smallest half-precision normal */
+   {6.1035156250000014E-5,                       6.1035156250000014E-5f,
+    {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x01", 9},
+    {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\x3F\x10\x00\x00\x00\x00\x00\x01", 9}},
+
+   /* 6.1035156249999993E-5 -- slightly smaller than smallest half-precision normal */
+   {6.1035156249999993E-5,  0.0f,
+    {"\xFB\x3F\x0F\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x3F\x0F\xFF\xFF\xFF\xFF\xFF\xFF", 9},
+    {"\xFB\x3F\x0F\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x3F\x0F\xFF\xFF\xFF\xFF\xFF\xFF", 9}},
+
+   /* 65504.0 -- largest possible half-precision */
+   {65504.0,                                     0.0f,
+    {"\xF9\x7B\xFF", 3},                         {"\xFB\x40\xEF\xFC\x00\x00\x00\x00\x00", 9},
+    {"\xF9\x7B\xFF", 3},                         {"\xF9\x7B\xFF", 3}},
+
+   /* 65504.1 -- exponent too large and too much precision to convert */
+   {65504.1,                                     0.0f,
+    {"\xFB\x40\xEF\xFC\x03\x33\x33\x33\x33", 9}, {"\xFB\x40\xEF\xFC\x03\x33\x33\x33\x33", 9},
+    {"\xFB\x40\xEF\xFC\x03\x33\x33\x33\x33", 9}, {"\xFB\x40\xEF\xFC\x03\x33\x33\x33\x33", 9}},
+
+    /* 65536.0 -- exponent too large but not too much precision for single */
+   {65536.0,                                     65536.0f,
+    {"\xFA\x47\x80\x00\x00", 5},                 {"\xFB\x40\xF0\x00\x00\x00\x00\x00\x00", 9},
+    {"\xFA\x47\x80\x00\x00", 5},                 {"\xFA\x47\x80\x00\x00", 5}},
+
+   /* 1.401298464324817e-45 -- smallest single subnormal */
+   {1.401298464324817e-45,                       1.40129846E-45f,
+    {"\xFA\x00\x00\x00\x01", 5},                 {"\xFB\x36\xA0\x00\x00\x00\x00\x00\x00", 9},
+    {"\xFA\x00\x00\x00\x01", 5},                 {"\xFA\x00\x00\x00\x01", 5}},
+
+   /* 5.8774717541114375E-39 -- slightly smaller than the smallest
+    // single normal */
+   {5.8774717541114375E-39,                      5.87747175E-39f,
+    {"\xFA\x00\x40\x00\x00", 5},                 {"\xFB\x38\x00\x00\x00\x00\x00\x00\x00", 9},
+    {"\xFA\x00\x40\x00\x00", 5},                 {"\xFA\x00\x40\x00\x00", 5}},
+
+   /* 1.1754942106924411e-38 -- largest single subnormal */
+   {1.1754942106924411E-38,                      1.17549421E-38f,
+    {"\xFA\x00\x7f\xff\xff", 5},                 {"\xFB\x38\x0f\xff\xff\xC0\x00\x00\x00", 9},
+    {"\xFA\x00\x7f\xff\xff", 5},                 {"\xFA\x00\x7f\xff\xff", 5} },
+
+   /* 1.1754943508222874E-38 -- slightly bigger than smallest single normal */
+   {1.1754943508222874E-38,                      0.0f,
+    {"\xFB\x38\x0f\xff\xff\xff\xff\xff\xff", 9}, {"\xFB\x38\x0f\xff\xff\xff\xff\xff\xff", 9},
+    {"\xFB\x38\x0f\xff\xff\xff\xff\xff\xff", 9}, {"\xFB\x38\x0f\xff\xff\xff\xff\xff\xff", 9}},
+
+   /* 1.1754943508222875e-38 -- smallest single normal */
+   {1.1754943508222875e-38,                      1.17549435E-38f,
+    {"\xFA\x00\x80\x00\x00", 5},                 {"\xFB\x38\x10\x00\x00\x00\x00\x00\x00", 9},
+    {"\xFA\x00\x80\x00\x00", 5},                 {"\xFA\x00\x80\x00\x00", 5}},
+
+   /* 1.1754943508222875e-38 -- slightly bigger than smallest single normal */
+   {1.1754943508222878e-38,                      0.0f,
+    {"\xFB\x38\x10\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\x38\x10\x00\x00\x00\x00\x00\x01", 9},
+    {"\xFB\x38\x10\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\x38\x10\x00\x00\x00\x00\x00\x01", 9}},
+
+   /* 16777216 -- converts to single without loss */
+   {16777216,                                    16777216,
+    {"\xFA\x4B\x80\x00\x00", 5},                 {"\xFB\x41\x70\x00\x00\x00\x00\x00\x00", 9},
+    {"\xFA\x4B\x80\x00\x00", 5},                 {"\xFA\x4B\x80\x00\x00", 5}},
+
+   /* 16777217 -- one more than above and fails conversion to single */
+   {16777217,                                    16777216,
+    {"\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}, {"\xFB\x41\x70\x00\x00\x10\x00\x00\x00", 9}},
+
+   /* 3.4028234663852886E+38 -- largest possible single normal */
+   {3.4028234663852886E+38,                      3.40282347E+38f,
+    {"\xFA\x7F\x7F\xFF\xFF", 5},                 {"\xFB\x47\xEF\xFF\xFF\xE0\x00\x00\x00", 9},
+    {"\xFA\x7F\x7F\xFF\xFF", 5},                 {"\xFA\x7F\x7F\xFF\xFF", 5}},
+
+   /* 3.402823466385289E+38 -- slightly larger than largest possible single */
+   {3.402823466385289E+38,                       0.0f,
+    {"\xFB\x47\xEF\xFF\xFF\xE0\x00\x00\x01", 9}, {"\xFB\x47\xEF\xFF\xFF\xE0\x00\x00\x01", 9},
+    {"\xFB\x47\xEF\xFF\xFF\xE0\x00\x00\x01", 9}, {"\xFB\x47\xEF\xFF\xFF\xE0\x00\x00\x01", 9}},
+
+   /* 3.402823669209385e+38 -- exponent larger by one than largest possible single */
+   {3.402823669209385e+38,                       0.0f,
+    {"\xFB\x47\xF0\x00\x00\x00\x00\x00\x00", 9}, {"\xFB\x47\xF0\x00\x00\x00\x00\x00\x00", 9},
+    {"\xFB\x47\xF0\x00\x00\x00\x00\x00\x00", 9}, {"\xFB\x47\xF0\x00\x00\x00\x00\x00\x00", 9}},
+
+   /* 5.0e-324 -- smallest double subnormal normal */
+   {5.0e-324,                                    0.0f,
+    {"\xFB\x00\x00\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\x00\x00\x00\x00\x00\x00\x00\x01", 9},
+    {"\xFB\x00\x00\x00\x00\x00\x00\x00\x01", 9}, {"\xFB\x00\x00\x00\x00\x00\x00\x00\x01", 9}},
+
+   /* 2.2250738585072009E−308 -- largest double subnormal */
+   {2.2250738585072009e-308,                     0.0f,
+    {"\xFB\x00\x0F\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x00\x0F\xFF\xFF\xFF\xFF\xFF\xFF", 9},
+    {"\xFB\x00\x0F\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x00\x0F\xFF\xFF\xFF\xFF\xFF\xFF", 9}},
+
+   /* 2.2250738585072014e-308 -- smallest double normal */
+   {2.2250738585072014e-308,                     0.0f,
+    {"\xFB\x00\x10\x00\x00\x00\x00\x00\x00", 9}, {"\xFB\x00\x10\x00\x00\x00\x00\x00\x00", 9},
+    {"\xFB\x00\x10\x00\x00\x00\x00\x00\x00", 9}, {"\xFB\x00\x10\x00\x00\x00\x00\x00\x00", 9}},
+
+   /* 1.7976931348623157E308 -- largest double normal */
+   {1.7976931348623157e308,                      0.0f,
+    {"\xFB\x7F\xEF\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x7F\xEF\xFF\xFF\xFF\xFF\xFF\xFF", 9},
+    {"\xFB\x7F\xEF\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\x7F\xEF\xFF\xFF\xFF\xFF\xFF\xFF", 9}},
+
+   /* List terminator */
+   {0.0, 0.0f, {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0} }
 };
 
 
-inline static bool CheckDouble(double d, uint64_t u)
+struct NaNTestCase {
+   uint64_t    uDouble;
+   uint32_t    uSingle;
+   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},                         {"\xFB\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9}},
+
+   /* 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
+ * data-driven by the above tables. It works better than tests below that
+ * it mostly replaces because it tests one number at a time, rather than
+ * putting them all in a map. It is much easier to debug test failures
+ * and to add new tests. */
+int32_t
+FloatValuesTests(void)
 {
-   return UsefulBufUtil_CopyDoubleToUint64(d) != u;
-}
+   unsigned int                 uTestIndex;
+   const struct DoubleTestCase *pTestCase;
+   const struct NaNTestCase    *pNaNTestCase;
+   MakeUsefulBufOnStack(        TestOutBuffer, 20);
+   UsefulBufC                   TestOutput;
+   QCBOREncodeContext           EnCtx;
+   QCBORError                   uErr;
+   QCBORDecodeContext           DCtx;
+   QCBORItem                    Item;
+   uint64_t                     uDecoded;
+#ifdef QCBOR_DISABLE_FLOAT_HW_USE
+   uint32_t                     uDecoded2;
+#endif
 
+   /* Test a variety of doubles */
+   for(uTestIndex = 0; DoubleTestCases[uTestIndex].Preferred.len != 0; uTestIndex++) {
+      pTestCase = &DoubleTestCases[uTestIndex];
 
-int32_t HalfPrecisionDecodeBasicTests(void)
-{
-   UsefulBufC HalfPrecision = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf);
+     // if(pTestCase->dNumber == 1.1754943508222874E-38) {
+         if(uTestIndex == 19) {
+         uErr = 99; /* For setting break points for particular tests */
+      }
 
-   QCBORDecodeContext DC;
-   QCBORDecode_Init(&DC, HalfPrecision, 0);
+      /* Number Encode of Preferred */
+      QCBOREncode_Init(&EnCtx, TestOutBuffer);
+      QCBOREncode_AddDouble(&EnCtx, pTestCase->dNumber);
+      uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
 
-   QCBORItem Item;
+      if(uErr != QCBOR_SUCCESS) {
+         return MakeTestResultCode(uTestIndex, 1, uErr);;
+      }
+      if(UsefulBuf_Compare(TestOutput, pTestCase->Preferred)) {
+         return MakeTestResultCode(uTestIndex, 1, 200);
+      }
 
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_MAP) {
-      return -1;
-   }
+      /* Number Encode of Not Preferred */
+      QCBOREncode_Init(&EnCtx, TestOutBuffer);
+      QCBOREncode_AddDoubleNoPreferred(&EnCtx, pTestCase->dNumber);
+      uErr = QCBOREncode_Finish(&EnCtx, &TestOutput);
 
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0) {
-      return -2;
-   }
+      if(uErr != QCBOR_SUCCESS) {
+         return MakeTestResultCode(uTestIndex, 2, uErr);;
+      }
+      if(UsefulBuf_Compare(TestOutput, pTestCase->NotPreferred)) {
+         return MakeTestResultCode(uTestIndex, 2, 200);
+      }
 
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
-      return -3;
-   }
-
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -INFINITY) {
-      return -4;
-   }
-
-   // TODO: NAN-related is this really converting right? It is carrying
-   // payload, but this confuses things.
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE || !isnan(Item.val.dfnum)) {
-      return -5;
-   }
-
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 1.0) {
-      return -6;
-   }
-
-   // Approximately 1/3
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.333251953125) {
-      return -7;
-   }
-
-   // Largest half-precision
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 65504.0) {
-      return -8;
-   }
-
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
-      return -9;
-   }
-
-   // Smallest half-precision subnormal
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.00000005960464477539063) {
-      return -10;
-   }
-
-   // Largest half-precision subnormal
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.00006097555160522461) {
-      return -11;
-   }
-
-   // Smallest half-precision normal
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.00006103515625) {
-      return -12;
-   }
-
-   // half-precision zero
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0) {
-      return -13;
-   }
-
-   // negative 2
-   QCBORDecode_GetNext(&DC, &Item);
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -2.0) {
-      return -14;
-   }
-
-   // TODO: NAN-related double check these four tests
-   QCBORDecode_GetNext(&DC, &Item); // qNaN
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
-      CheckDouble(Item.val.dfnum, 0x7ff8000000000000ULL)) {
-      return -15;
-   }
-   QCBORDecode_GetNext(&DC, &Item); // sNaN
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
-      CheckDouble(Item.val.dfnum, 0x7ff0000000000001ULL)) {
-      return -16;
-   }
-   QCBORDecode_GetNext(&DC, &Item); // qNaN with payload 0x0f
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
-      CheckDouble(Item.val.dfnum, 0x7ff800000000000fULL)) {
-      return -17;
-   }
-   QCBORDecode_GetNext(&DC, &Item); // sNaN with payload 0x0f
-   if(Item.uDataType != QCBOR_TYPE_DOUBLE ||
-      CheckDouble(Item.val.dfnum, 0x7ff000000000000fULL)) {
-      return -18;
-   }
-
-   if(QCBORDecode_Finish(&DC)) {
-      return -19;
-   }
-
-   return 0;
-}
-
-
-
-
-int32_t HalfPrecisionAgainstRFCCodeTest(void)
-{
-    for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 60) {
-        unsigned char x[2];
-        x[1] = (uint8_t)(uHalfP & 0xff);
-        x[0] = (uint8_t)(uHalfP >> 8); // uHalfP is always less than 0xffff
-        double d = decode_half(x);
-
-        // Contruct the CBOR for the half-precision float by hand
-        UsefulBuf_MAKE_STACK_UB(__xx, 3);
-        UsefulOutBuf UOB;
-        UsefulOutBuf_Init(&UOB, __xx);
-
-        const uint8_t uHalfPrecInitialByte = (uint8_t)(HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5)); // 0xf9
-        UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
-        UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
-
-        // Now parse the hand-constructed CBOR. This will invoke the
-        // conversion to a float
-        QCBORDecodeContext DC;
-        QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
-
-        QCBORItem Item;
-
-        QCBORDecode_GetNext(&DC, &Item);
-        if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
-            return -1;
-        }
-
-        //printf("%04x  QCBOR:%15.15f  RFC: %15.15f (%8x)\n",
-        //       uHalfP, Item.val.fnum, d , UsefulBufUtil_CopyFloatToUint32(d));
-
-        if(isnan(d)) {
-            // The RFC code uses the native instructions which may or may not
-            // handle sNaN, qNaN and NaN payloads correctly. This test just
-            // makes sure it is a NaN and doesn't worry about the type of NaN
+      /* Number Decode of Preferred */
+      QCBORDecode_Init(&DCtx, pTestCase->Preferred, 0);
+      uErr = QCBORDecode_GetNext(&DCtx, &Item);
+      if(uErr != QCBOR_SUCCESS) {
+         return MakeTestResultCode(uTestIndex, 3, uErr);;
+      }
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+      if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
+         return MakeTestResultCode(uTestIndex, 4, 0);
+      }
+      if(isnan(pTestCase->dNumber)) {
+         if(!isnan(Item.val.dfnum)) {
+            return MakeTestResultCode(uTestIndex, 5, 0);
+         }
+      } else {
+         if(Item.val.dfnum != pTestCase->dNumber) {
+            return MakeTestResultCode(uTestIndex, 6, 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) {
+         if(Item.uDataType != QCBOR_TYPE_FLOAT) {
+            return MakeTestResultCode(uTestIndex, 4, 0);
+         }
+         if(isnan(pTestCase->dNumber)) {
+            if(!isnan(Item.val.fnum)) {
+               return MakeTestResultCode(uTestIndex, 5, 0);
+            }
+         } else {
+            if(Item.val.fnum != pTestCase->fNumber) {
+               return MakeTestResultCode(uTestIndex, 6, 0);
+            }
+         }
+      } else {
+         if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
+            return MakeTestResultCode(uTestIndex, 4, 0);
+         }
+         if(isnan(pTestCase->dNumber)) {
             if(!isnan(Item.val.dfnum)) {
-                return -3;
+               return MakeTestResultCode(uTestIndex, 5, 0);
             }
-        } else {
-            if(Item.val.dfnum != d) {
-                return -2;
+         } else {
+            if(Item.val.dfnum != pTestCase->dNumber) {
+               return MakeTestResultCode(uTestIndex, 6, 0);
             }
-        }
-    }
-    return 0;
-}
+         }
+      }
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
 
+      /* Number Decode of Not Preferred */
+      QCBORDecode_Init(&DCtx, pTestCase->NotPreferred, 0);
+      uErr = QCBORDecode_GetNext(&DCtx, &Item);
+      if(uErr != QCBOR_SUCCESS) {
+         return MakeTestResultCode(uTestIndex, 7, uErr);;
+      }
+      if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
+         return MakeTestResultCode(uTestIndex, 8, 0);
+      }
+      if(isnan(pTestCase->dNumber)) {
+         if(!isnan(Item.val.dfnum)) {
+            return MakeTestResultCode(uTestIndex, 9, 0);
+         }
+      } else {
+         if(Item.val.dfnum != pTestCase->dNumber) {
+            return MakeTestResultCode(uTestIndex, 10, 0);
+         }
+      }
 
-/*
- Expected output from preferred serialization of some of floating-point numbers
-{"zero": 0.0,
- "negative zero": -0.0,
- "infinitity": Infinity,
- "negative infinitity": -Infinity,
- "NaN": NaN,
- "one": 1.0,
- "one third": 0.333251953125,
- "largest half-precision": 65504.0,
- "largest half-precision point one": 65504.1,
- "too-large half-precision": 65536.0,
- "smallest half subnormal": 5.960464477539063e-8,
- "smallest half normal": 0.00006103515625,
- "smallest half normal plus": 0.00006103515625000001,
- "smallest normal minus": 0.000030517578125,
- "largest single": 3.4028234663852886e+38,
- "largest single plus": 6.805646932770577e+38,
- "smallest single": 1.1754943508222875e-38,
- "smallest single plus": 1.1754943508222878e-38,
- "smallest single minus": 1.1754943508222874e-38,
- "smallest single minus more": 5.877471754111438e-39,
- 3: -2.0, "single precision": 16777216.0,
- "single with precision loss": 16777217.0,
- 1: "fin"}
- */
-static const uint8_t spExpectedSmallest[] = {
-   0xB8, 0x1A,
-      0x64, 0x7A, 0x65, 0x72, 0x6F,
-      0xF9, 0x00, 0x00,
-
-      0x6D, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7A,
-         0x65, 0x72, 0x6F,
-      0xF9, 0x80, 0x00,
-
-      0x6A, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
-      0xF9, 0x7C, 0x00,
-
-      0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69,
-         0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
-      0xF9, 0xFC, 0x00,
-
-      0x63, 0x4E, 0x61, 0x4E,
-      0xF9, 0x7E, 0x00,
-
-      0x63, 0x6F, 0x6E, 0x65,
-      0xF9, 0x3C, 0x00,
-
-      0x69, 0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
-      0xF9, 0x35, 0x55,
-
-      0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61,
-         0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69,
-         0x6F, 0x6E,
-      0xF9, 0x7B, 0xFF,
-
-      0x78, 0x20, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68,
-         0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73,
-         0x69, 0x6F, 0x6E, 0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20,
-         0x6F, 0x6E, 0x65,
-      0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33,
-
-      0x78, 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65,
-         0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63,
-         0x69, 0x73, 0x69, 0x6F, 0x6E,
-      0xFA, 0x47, 0x80, 0x00, 0x00,
-
-      0x77, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74,
-         0x20, 0x68, 0x61, 0x6C, 0x66, 0x20, 0x73, 0x75, 0x62, 0x6E,
-         0x6F, 0x72, 0x6D, 0x61, 0x6C,
-      0xFA, 0x33, 0x80, 0x00, 0x00,
-
-      0x74, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x68,
-         0x61, 0x6C, 0x66, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
-      0xF9, 0x04, 0x00,
-
-      0x78, 0x19, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
-         0x68, 0x61, 0x6C, 0x66, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61,
-         0x6C, 0x20, 0x70, 0x6C, 0x75, 0x73,
-      0xFB, 0x3F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-
-      0x75, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E,
-         0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x6D, 0x69, 0x6E,
-         0x75, 0x73,
-      0xFB, 0x3F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-
-      0x75, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E,
-         0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x6D, 0x69, 0x6E, 0x75,
-         0x73,
-      0xFA, 0x38, 0x00, 0x00, 0x00,
-
-      0x6E, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69,
-         0x6E, 0x67, 0x6C, 0x65,
-      0xFA, 0x7F, 0x7F, 0xFF, 0xFF,
-
-      0x73, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69,
-         0x6E,0x67, 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73,
-      0xFB, 0x47, 0xEF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x01,
-
-      0x73, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x69,
-         0x6E, 0x67, 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73,
-      0xFB, 0x47, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
-
-      0x6F, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73,
-         0x69, 0x6E, 0x67, 0x6C, 0x65,
-      0xFA, 0x00, 0x80, 0x00, 0x00,
-
-      0x74, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73,
-         0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x70, 0x6C, 0x75, 0x73,
-      0xFB, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-
-      0x75, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73,
-         0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x6D, 0x69, 0x6E, 0x75,
-         0x73,
-      0xFB, 0x38, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-
-      0x78, 0x1A, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
-         0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x6D, 0x69, 0x6E,
-         0x75, 0x73, 0x20, 0x6D, 0x6F, 0x72, 0x65,
-      0xFB, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-
-      0x03,
-      0xF9, 0xC0, 0x00,
-
-      0x70, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x70, 0x72, 0x65,
-         0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
-      0xFA, 0x4B, 0x80, 0x00, 0x00,
-
-      0x78, 0x1A, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69,
-         0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69,
-         0x6F, 0x6E, 0x20, 0x6C, 0x6F, 0x73, 0x73,
-      0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
-
-      0x01,
-      0x63, 0x66, 0x69, 0x6E
-};
-
-
-/*
- Makes a double from a uint64_t by copying the bits, not
- by converting the value.
- */
-#define MAKE_DOUBLE(x) UsefulBufUtil_CopyUint64ToDouble(x)
-
-
-int32_t DoubleAsSmallestTest(void)
-{
-   UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, sizeof(spExpectedSmallest));
-
-   QCBOREncodeContext EC;
-   QCBOREncode_Init(&EC, EncodedHalfsMem);
-   QCBOREncode_OpenMap(&EC);
-
-   // Many of these are from
-   // https://en.wikipedia.org/wiki/Half-precision_floating-point_format
-   // and
-   // https://en.wikipedia.org/wiki/Single-precision_floating-point_format
-
-   // F9 0000                              # primitive(0)
-   QCBOREncode_AddDoubleToMap(&EC, "zero", 0.00);
-
-   // F9 8000                              # primitive(0)
-   QCBOREncode_AddDoubleToMap(&EC, "negative zero", -0.00);
-
-   // F9 7C00                              # primitive(31744)
-   QCBOREncode_AddDoubleToMap(&EC, "infinitity", INFINITY);
-
-   // F9 FC00                              # primitive(64512)
-   QCBOREncode_AddDoubleToMap(&EC, "negative infinitity", -INFINITY);
-
-   // F9 7E00                              # primitive(32256)
-   QCBOREncode_AddDoubleToMap(&EC, "NaN", NAN);
-
-   // TODO: test a few NaN variants
-
-   // F9 3C00                              # primitive(15360)
-   QCBOREncode_AddDoubleToMap(&EC, "one", 1.0);
-
-   // F9 3555                              # primitive(13653)
-   QCBOREncode_AddDoubleToMap(&EC, "one third", 0.333251953125);
-
-   // 65504.0, converts to the large possible half-precision.
-   // 0xF9, 0x7B, 0xFF,
-   QCBOREncode_AddDoubleToMap(&EC, "largest half-precision", 65504.0);
-
-   // 65504.1, the double that has both to large an exponent and too
-   // much precision, so no conversion.
-   // 0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33,
-   QCBOREncode_AddDoubleToMap(&EC, "largest half-precision point one", 65504.1);
-
-   // 65536.0 has an exponent of 16, which is larger than 15, the
-   // largest half-precision exponent. It is the exponent, not
-   // precision loss that prevents conversion to half. It does convert
-   // to single precision.
-   // 0xFA, 0x47, 0x80, 0x00, 0x00,
-   QCBOREncode_AddDoubleToMap(&EC, "too-large half-precision", 65536.0);
-
-   // 5.9604644775390625E-8, the smallest possible half-precision
-   // subnormal, digitis are lost converting to half, but not
-   // when converting to a single
-   // 0xFA, 0x33, 0x80, 0x00, 0x00,
-   QCBOREncode_AddDoubleToMap(&EC,
-                              "smallest half subnormal",
-                              MAKE_DOUBLE(0x3e70000000000000));
-
-   // 0.00006103515625, the double value that converts to the smallest
-   // possible half-precision normal.  which is what should appear in
-   // the output.
-   // 0xF9, 0x04, 0x00,
-   QCBOREncode_AddDoubleToMap(&EC,
-                              "smallest half normal",
-                              MAKE_DOUBLE(0x3f10000000000000));
-
-   // 0.000061035156250000014 ,the double value that is a tiny bit
-   // greater than smallest possible half-precision normal. It will be
-   // output as a double because converting it will reduce precision.
-   // 0xFB, 0x3F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-   QCBOREncode_AddDoubleToMap(&EC,
-                              "smallest half normal plus",
-                              MAKE_DOUBLE(0x3f10000000000001));
-
-   // 0.000061035156249999993, the double value that is a tiny bit
-   // smaller than the smallest half-precision normal. This will fail
-   // to convert to a half-precision because both the exponent is too
-   // small and the precision is too large for a half-precision.
-   // 0xFB, 0x3F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-   QCBOREncode_AddDoubleToMap(&EC,
-                              "smallest normal minus",
-                              MAKE_DOUBLE(0x3f0fffffffffffff));
-
-   // 0.000030517578125, the double value that is too small to fit
-   // into a half-precision because the exponent won't fit, not
-   // because precision would be lost. (This would fit into a
-   // half-precision subnormal, but there is no converstion to
-   // that). This ends up encoded as a single-precision.
-   // 0xFA, 0x38, 0x00, 0x00, 0x00,
-   QCBOREncode_AddDoubleToMap(&EC,
-                              "smallest normal minus",
-                              MAKE_DOUBLE(0x3f00000000000000));
-
-   // 3.4028234664e38, the value that converts to the largest possible
-   // single-precision.
-   // 0xFA, 0x7F, 0x7F, 0xFF, 0xFF,
-   QCBOREncode_AddDoubleToMap(&EC,
-                              "largest single",
-                              MAKE_DOUBLE(0x47efffffe0000000));
-
-   // 3.402823466385289E38, sightly larger than the largest possible
-   // possible precision.  Conversion fails because precision would be
-   // lost.
-   // 0xFB, 0x47, 0xEF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x01,
-   QCBOREncode_AddDoubleToMap(&EC,
-                              "largest single plus",
-                              MAKE_DOUBLE(0x47efffffe0000001));
-
-   // 6.8056469327705772E38, slightly more larger than the largers
-   // possible single precision.  Conversion fails because exponent is
-   // too large.
-   // 0xFB, 0x47, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
-   QCBOREncode_AddDoubleToMap(&EC,
-                              "largest single plus",
-                              MAKE_DOUBLE(0x47ffffffe0000000));
-
-   // 1.1754943508222875E-38, The double value that converts to the
-   // smallest possible single-precision normal
-   // 0xFA, 0x00, 0x80, 0x00, 0x00,
-   QCBOREncode_AddDoubleToMap(&EC,
-                              "smallest single",
-                              MAKE_DOUBLE(0x3810000000000000));
-
-   // 1.1754943508222878E-38, double value that is slightly larger
-   // than the smallest single-precision normal. Conversion fails
-   // because of precision
-   // 0xFB, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-   QCBOREncode_AddDoubleToMap(&EC,
-                              "smallest single plus",
-                              MAKE_DOUBLE(0x3810000000000001));
-
-   // 1.1754943508222874E-38, slightly smaller than the smallest
-   // single-precision normal.  Conversion fails because of precision
-   // 0xFB, 0x38, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-   QCBOREncode_AddDoubleToMap(&EC,
-                              "smallest single minus",
-                              MAKE_DOUBLE(0x380fffffffffffff));
-
-   // 5.8774717541114375E-39, slightly smaller than the smallest
-   // single-precision normal.  Conversion fails because the exponent
-   // is too small.
-   // 0xFB, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-   QCBOREncode_AddDoubleToMap(&EC,
-                              "smallest single minus more",
-                              MAKE_DOUBLE(0x3800000000000000));
-
-   // Just -2, which converts to a negative half-precision
-   // F9 C000                              # primitive(49152)
-   QCBOREncode_AddDoubleToMapN(&EC, 3, -2.0);
-
-   // 16777216, No precision loss converting to single
-   // FA 4B800000                          # primitive(1266679808)
-   QCBOREncode_AddDoubleToMap(&EC, "single precision", 16777216);
-
-   // 16777217, One more than above. Too much precision for a single
-   // so no conversion.
-   // 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
-   QCBOREncode_AddDoubleToMap(&EC, "single with precision loss", 16777217);
-
-   // Just a convenient marker when cutting and pasting encoded CBOR
-   QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
-
-   QCBOREncode_CloseMap(&EC);
-
-   UsefulBufC EncodedHalfs;
-   QCBORError uErr = QCBOREncode_Finish(&EC, &EncodedHalfs);
-   if(uErr) {
-      return -1;
    }
 
-   if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
-      return -3;
+   /* 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_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_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 Preferred */
+      QCBORDecode_Init(&DCtx, pNaNTestCase->Preferred, 0);
+      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);
+      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);
+      }
    }
 
    return 0;
 }
+
+
+
+/* Public function. See float_tests.h */
+int32_t 
+HalfPrecisionAgainstRFCCodeTest(void)
+{
+   QCBORItem          Item;
+   QCBORDecodeContext DC;
+   unsigned char      pbHalfBytes[2];
+   uint8_t            uHalfPrecInitialByte;
+   double             d;
+   UsefulBuf_MAKE_STACK_UB(EncodedBytes, 3);
+   UsefulOutBuf      UOB;
+   uint32_t          uHalfP;
+
+
+   for(uHalfP = 0; uHalfP < 0xffff; uHalfP += 60) {
+      pbHalfBytes[1] = (uint8_t)(uHalfP & 0xff);
+      pbHalfBytes[0] = (uint8_t)(uHalfP >> 8); /* uHalfP is always less than 0xffff */
+      d = decode_half(pbHalfBytes);
+
+      /* Construct the CBOR for the half-precision float by hand */
+      UsefulOutBuf_Init(&UOB, EncodedBytes);
+
+      uHalfPrecInitialByte = (uint8_t)(HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5)); /* 0xf9 */
+      UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); /* initial byte */
+      UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);   /* argument */
+
+      /* Now parse the hand-constructed CBOR. This will invoke the
+       * conversion to a float
+       */
+      QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
+      QCBORDecode_GetNext(&DC, &Item);
+      if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
+         return -1;
+      }
+
+      if(isnan(d)) {
+         /* The RFC code uses the native instructions which may or may not
+          * handle sNaN, qNaN and NaN payloads correctly. This test just
+          * makes sure it is a NaN and doesn't worry about the type of NaN
+          */
+         if(!isnan(Item.val.dfnum)) {
+            return -3;
+         }
+      } else {
+         if(Item.val.dfnum != d) {
+            return -2;
+         }
+      }
+   }
+   return 0;
+}
+
 #endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
 
 
@@ -700,17 +683,27 @@
          0x18, 0x6A,
           0xFA, 0x00, 0x00, 0x00, 0x00};
 
-int32_t GeneralFloatEncodeTests(void)
+
+/* Public function. See float_tests.h */
+int32_t
+GeneralFloatEncodeTests(void)
 {
+   /* See FloatNumberTests() for tests that really cover lots of float values.
+    * Add new tests for new values or decode modes there. 
+    * This test is primarily to cover all the float encode methods. */
+
+   UsefulBufC Encoded;
    UsefulBufC ExpectedFloats;
+   QCBORError uErr;
+
 #ifndef QCBOR_DISABLE_PREFERRED_FLOAT
    UsefulBuf_MAKE_STACK_UB(OutBuffer, sizeof(spExpectedFloats));
    ExpectedFloats = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloats);
-   (void)spExpectedFloatsNoHalf; // Avoid unused variable error
+   (void)spExpectedFloatsNoHalf; /* Avoid unused variable error */
 #else
    UsefulBuf_MAKE_STACK_UB(OutBuffer, sizeof(spExpectedFloatsNoHalf));
    ExpectedFloats = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedFloatsNoHalf);
-   (void)spExpectedFloats; // Avoid unused variable error
+   (void)spExpectedFloats; /* Avoid unused variable error */
 #endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
 
    QCBOREncodeContext EC;
@@ -744,8 +737,7 @@
    QCBOREncode_CloseMap(&EC);
    QCBOREncode_CloseArray(&EC);
 
-   UsefulBufC Encoded;
-   QCBORError uErr = QCBOREncode_Finish(&EC, &Encoded);
+   uErr = QCBOREncode_Finish(&EC, &Encoded);
    if(uErr) {
       return -1;
    }
@@ -757,25 +749,15 @@
    return 0;
 }
 
-
-/* returns 0 if equivalent, non-zero if not equivalent */
-static int CHECK_EXPECTED_DOUBLE(double val, double expected)
-{
-   double diff = val - expected;
-
-   diff = fabs(diff);
-
-   if(diff > 0.000001) {
-      return 1;
-   } else {
-      return 0;
-   }
-}
 #endif /* USEFULBUF_DISABLE_ALL_FLOAT */
 
 
-int32_t GeneralFloatDecodeTests(void)
+/* Public function. See float_tests.h */
+int32_t 
+GeneralFloatDecodeTests(void)
 {
+   /* See FloatNumberTests() for tests that really cover lots of float values */
+
    QCBORItem          Item;
    QCBORError         uErr;
    QCBORDecodeContext DC;
@@ -872,10 +854,10 @@
 #ifndef USEFULBUF_DISABLE_ALL_FLOAT
 #ifndef QCBOR_DISABLE_FLOAT_HW_USE
       || Item.uDataType != QCBOR_TYPE_DOUBLE
-      || CHECK_EXPECTED_DOUBLE(3.14, Item.val.dfnum)
+      || 3.1400001049041748 != Item.val.dfnum
 #else /* QCBOR_DISABLE_FLOAT_HW_USE */
       || Item.uDataType != QCBOR_TYPE_FLOAT
-      || CHECK_EXPECTED_DOUBLE(3.14, Item.val.fnum)
+      || 3.140000f != Item.val.fnum
 #endif /* QCBOR_DISABLE_FLOAT_HW_USE */
 #else /* USEFULBUF_DISABLE_ALL_FLOAT */
       || Item.uDataType != QCBOR_TYPE_NONE
@@ -893,7 +875,7 @@
       || Item.val.dfnum != 0.0
 #else /* QCBOR_DISABLE_FLOAT_HW_USE */
       || Item.uDataType != QCBOR_TYPE_FLOAT
-      || Item.val.fnum != 0.0
+      || Item.val.fnum != 0.0f
 #endif /* QCBOR_DISABLE_FLOAT_HW_USE */
 #else /* USEFULBUF_DISABLE_ALL_FLOAT */
       || Item.uDataType != QCBOR_TYPE_NONE