Full big num implementation with preferred serialization and 65-bit negs (#219)

Encoding and decoding of negative big numbers now takes into account the offset of 1 for all CBOR negative numbers. Also, big numbers are encoded with preferred serialization -- when they can be encoded with type 0 and type 1 integers, they are. Encoding and decoding big numbers is no longer a pass through for tagging a binary string.

This is an incompatible change with QCBOR 1.x. A mode configuration call is added to return to the 1.x behavior if desired.

This is because of the realization in work on 65-bit negative numbers that big numbers need preferred serialization.

This affects big floats and decimal fractions when the mantissa is a big number.

New methods to encode big numbers with non-preferred serialization are added.

A new method is added to process a raw big number for the offset of one for negatives. This is outside of spiffy decode. It does a little big number arithmetic.

dCBOR numeric reduction now includes 65-bit integers both for number encoding and decoding.



* Checkpoint work on 65-bit negs

* check point ... mostly working

* Checkpoint work on preferred bignums

* Tests passing...

* bignum encode improved to near completion

* Fix #ifdef fan out and error condition bug

* Code tidiness

* debugging unreplicatable CI failure

* v1 compat mode; EAM encode test fan out starting

* debugging ci...

* hacking ci failures

* CI failure hacking

* CI hacking

* Clean up some left over junk

* more leftover junk

* Clean up test leftovers

* Fixes for #ifdef fan out

* Fix conversion of uint to zero, even though it's never used

* REmove more junk

---------

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/test/float_tests.c b/test/float_tests.c
index ce078d4..d59241f 100644
--- a/test/float_tests.c
+++ b/test/float_tests.c
@@ -281,10 +281,10 @@
     {"\xFB\xC3\xDF\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\xFB\xC3\xDF\xFF\xFF\xFF\xFF\xFF\xFF", 9},
     {"\xFB\xC3\xDF\xFF\xFF\xFF\xFF\xFF\xFF", 9}, {"\x3B\x7F\xFF\xFF\xFF\xFF\xFF\xFB\xFF", 9}},
 
-   /* -18446742974197924000.0.0 -- large negative that converts to float, but too large for int64 */
-   {-18446742974197924000.0,                     -18446742974197924000.0f,
+   /* -18446742974197923840.0 -- large negative that converts to float, but too large for int64 */
+   {-18446742974197923840.0,                     -18446742974197923840.0f,
     {"\xFA\xDF\x7F\xFF\xFF",                 5}, {"\xFB\xC3\xEF\xFF\xFF\xE0\x00\x00\x00", 9},
-    {"\xFA\xDF\x7F\xFF\xFF",                 5}, {"\xFA\xDF\x7F\xFF\xFF",                 5}},
+    {"\xFA\xDF\x7F\xFF\xFF",                 5}, {"\x3B\xFF\xFF\xFE\xFF\xFF\xFF\xFF\xFF", 9}},
 
    /* 3.4028234663852886E+38 -- largest possible single */
    {3.4028234663852886E+38,                      3.40282347E+38f,
@@ -414,7 +414,7 @@
    for(uTestIndex = 0; FloatTestCases[uTestIndex].Preferred.len != 0; uTestIndex++) {
       pTestCase = &FloatTestCases[uTestIndex];
 
-      if(uTestIndex == 34) {
+      if(uTestIndex == 40) {
          uDecoded = 1;
       }
 
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 191c448..202be19 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -79,8 +79,11 @@
    return (int32_t)uCode;
 }
 
+
 /*
    [
+      -18446744073709551616,
+      -18446744073709551615,
       -9223372036854775808,
       -4294967297,
       -4294967296,
@@ -175,13 +178,13 @@
    if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
       return (int32_t)nCBORError;
    if(Item.uDataType != QCBOR_TYPE_65BIT_NEG_INT ||
-      Item.val.uint64 != 18446744073709551615ULL)
+      Item.val.uint64 != 0xffffffffffffffff)
       return -1;
 
    if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
       return (int32_t)nCBORError;
    if(Item.uDataType != QCBOR_TYPE_65BIT_NEG_INT ||
-      Item.val.uint64 != 18446744073709551614ULL)
+      Item.val.uint64 != 0xfffffffffffffffe)
       return -1;
 
    if((nCBORError = QCBORDecode_GetNext(pDCtx, &Item)))
@@ -2558,7 +2561,7 @@
       }
 #endif /* QCBOR_DISABLE_INDEFINITE_LENGTH_STRINGS */
 
-      if(nIndex == 54) {
+      if(nIndex == 55) {
          uCBORError = 9; /* For setting break points */
       }
 
@@ -4648,15 +4651,155 @@
        0xC3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
 
+
+struct BignumDecodeTest {
+   const char *szDescription;
+   UsefulBufC  Encoded;
+   QCBORError  uErr;
+   UsefulBufC  ExpectedBigNum;
+   bool        bExpectedSign;
+};
+
 #ifndef QCBOR_DISABLE_TAGS
+static struct BignumDecodeTest BignumDecodeTests[] = {
+   {
+      "-18446744073709551617",
+      {"\xC3\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+      QCBOR_SUCCESS,
+      {"\x01\x00\x00\x00\x00\x00\x00\x00\x01", 9},
+      true
+   },
+   {
+      "-18446744073709551616 preferred",
+      {"\x3B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+      QCBOR_SUCCESS,
+      {"\x01\x00\x00\x00\x00\x00\x00\x00\x00", 9},
+      true
+   },
+   {
+      "-18446744073709551616 as big num",
+      {"\xC3\x48\xff\xff\xff\xff\xff\xff\xff\xff", 10},
+      QCBOR_SUCCESS,
+      {"\x01\x00\x00\x00\x00\x00\x00\x00\x00", 9},
+      true
+   },
+   {
+      "-9223372036854775808  -(2^63)",
+      {"\x3B\x7f\xff\xff\xff\xff\xff\xff\xff", 9},
+      QCBOR_SUCCESS,
+      {"\x80\x00\x00\x00\x00\x00\x00\x00", 8},
+      true
+   },
+
+   {
+      "Preferred -1",
+      {"\x20", 1},
+      QCBOR_SUCCESS,
+      {"\x01", 1},
+      true
+   },
+   {
+      "bignum -1",
+      {"\xc3\x42\x00\x00", 4},
+      QCBOR_SUCCESS,
+      {"\x01", 1},
+      true
+   },
+   {
+      "bignum -1 empty buffer",
+      {"\xc3\x40", 2},
+      QCBOR_SUCCESS,
+      {"\x01", 1},
+      true
+   },
+   {
+      "Preferred Zero",
+      {"\x00", 1},
+      QCBOR_SUCCESS,
+      {"\x00", 1},
+      false
+   },
+   {
+      "Bignum zero",
+      {"\xC2\x40", 2},
+      QCBOR_SUCCESS,
+      {"\x00", 1},
+      false
+   },
+   {
+      "Bignum zero with leading zeros",
+      {"\xC2\x43\x00\x00\x00", 5},
+      QCBOR_SUCCESS,
+      {"\x00", 1},
+      false
+   },
+   {
+      "Preferred one",
+      {"\x01", 1},
+      QCBOR_SUCCESS,
+      {"\x01", 1},
+      false
+   },
+   {
+      "Bignum one",
+      {"\xc2\x41\x01", 3},
+      QCBOR_SUCCESS,
+      {"\x01", 1},
+      false
+   },
+   {
+      "512 with leading zeros",
+      {"\x1A\x00\x00\x02\x00", 5},
+      QCBOR_SUCCESS,
+      {"\x02\x00", 2},
+      false
+   },
+   {
+      "512 with leading zeros again",
+      {"\xc2\x46\x00\x00\x00\x00\x02\x00", 8},
+      QCBOR_SUCCESS,
+      {"\x02\x00", 2},
+      false
+   },
+   {
+      "1297093868730187896 (a byte pattern with zeros and different digits",
+      {"\x1B\x12\x00\x34\x00\x56\x00\x00\x78", 9},
+      QCBOR_SUCCESS,
+      {"\x12\x00\x34\x00\x56\x00\x00\x78", 8},
+      false
+   },
+   {
+      "Preferred UINT64_MAX",
+      {"\x1B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+      QCBOR_SUCCESS,
+      {"\xff\xff\xff\xff\xff\xff\xff\xff", 8},
+      false
+   },
+   {
+      "Bignum UINT64_MAX",
+      {"\xC2\x48\xff\xff\xff\xff\xff\xff\xff\xff", 10},
+      QCBOR_SUCCESS,
+      {"\xff\xff\xff\xff\xff\xff\xff\xff", 8},
+      false
+   },
+   {
+      "UINT64_MAX + 1",
+      {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+      QCBOR_SUCCESS,
+      {"\x01\x00\x00\x00\x00\x00\x00\x00\x00", 9},
+      false
+   }
+};
+
+
 /* The expected big num */
 static const uint8_t spBigNum[] = {
    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00};
-#endif /* QCBOR_DISABLE_TAGS */
+#endif /* ! QCBOR_DISABLE_TAGS */
 
 
-int32_t BignumParseTest(void)
+int32_t BignumDecodeTest(void)
 {
    QCBORDecodeContext DCtx;
    QCBORItem Item;
@@ -4732,10 +4875,72 @@
       UsefulBuf_Compare(Item.val.bigNum, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBigNum))){
       return -14;
    }
-
-
 #endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
 
+
+   unsigned                  uTestIndex;
+   unsigned                  uTestCount;
+   struct BignumDecodeTest  *pTest;
+   QCBORError                uErr;
+   UsefulBuf_MAKE_STACK_UB(  BignumBuf, 200);
+   UsefulBufC                ResultBigNum;
+   bool                      bIsNeg;
+
+   uTestCount = (int)C_ARRAY_COUNT(BignumDecodeTests, struct BignumDecodeTest);
+
+   for(uTestIndex = 0; uTestIndex < uTestCount; uTestIndex++) {
+      pTest = &BignumDecodeTests[uTestIndex];
+
+      if(uTestIndex == 13) {
+         bIsNeg = false; /* Line of code so a break point can be set. */
+      }
+
+      QCBORDecode_Init(&DCtx, pTest->Encoded, 0);
+      uErr = QCBORDecode_GetNext(&DCtx, &Item);
+      if(uErr != QCBOR_SUCCESS) {
+         return MakeTestResultCode(uTestIndex, 1, uErr);
+      }
+
+      uErr = QCBORDecode_BignumPreferred(Item, BignumBuf, &ResultBigNum, &bIsNeg);
+      if(uErr != pTest->uErr) {
+         return MakeTestResultCode(uTestIndex, 2, uErr);
+      }
+
+      if(uErr != QCBOR_SUCCESS) {
+         continue; /* This test passed */
+      }
+
+      if(UsefulBuf_Compare(ResultBigNum, pTest->ExpectedBigNum)) {
+         return MakeTestResultCode(uTestIndex, 3, 0);
+      }
+
+      if(bIsNeg != pTest->bExpectedSign) {
+         return MakeTestResultCode(uTestIndex, 4, 0);
+      }
+
+      uErr = QCBORDecode_BignumPreferred(Item, (UsefulBuf){NULL, 200}, &ResultBigNum, &bIsNeg);
+      if(ResultBigNum.len != pTest->ExpectedBigNum.len) {
+         return MakeTestResultCode(uTestIndex, 5, uErr);
+      }
+
+      QCBORDecode_Init(&DCtx, pTest->Encoded, 0);
+      QCBORDecode_GetBigNumPreferred(&DCtx, QCBOR_TAG_REQUIREMENT_TAG, BignumBuf,  &ResultBigNum, &bIsNeg);
+      uErr = QCBORDecode_GetError(&DCtx);
+      if(uErr != QCBOR_SUCCESS) {
+         return MakeTestResultCode(uTestIndex, 6, uErr);
+      }
+
+      if(UsefulBuf_Compare(ResultBigNum, pTest->ExpectedBigNum)) {
+         return MakeTestResultCode(uTestIndex, 7, 0);
+      }
+
+      if(bIsNeg != pTest->bExpectedSign) {
+         return MakeTestResultCode(uTestIndex, 8, 0);
+      }
+
+   }
+
+
 #else
 
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_TAGS_DISABLED) {
@@ -7732,6 +7937,10 @@
          return (int32_t)(3333+nIndex);
       }
 
+      if(nIndex == 21) {
+         uInt = 99; // For break point only
+      }
+
       int64_t nInt;
       QCBORDecode_GetInt64ConvertAll(&DCtx, 0xffff, &nInt);
       if(QCBORDecode_GetError(&DCtx) != pF->uErrorInt64) {
@@ -10109,7 +10318,7 @@
    QCBORDecode_Init(&DCtx,UsefulBuf_FROM_SZ_LITERAL("\xa2\x00\x00\x00\x00"), QCBOR_DECODE_MODE_CDE);
    QCBORDecode_EnterMap(&DCtx, &Item);
    if(QCBORDecode_GetError(&DCtx) != QCBOR_ERR_DUPLICATE_LABEL) {
-      return 5000;
+      return -5000;
    }
 
    return ProcessDecodeFailures(DecodeConformanceFailures,
@@ -10187,39 +10396,98 @@
       {0, UINT64_MAX, 0}
    },
    {
-      "INT64_MIN",
+      "Largest float that is also representable as int64_t",
+      {"\x3B\x7f\xff\xff\xff\xff\xff\xfb\xff", 9},
+      QCBOR_SUCCESS,
+      QCBOR_TYPE_INT64,
+      {-9223372036854774784, 0, 0}
+   },
+   {
+      "-9223372036854775807",
+      {"\x3B\x7f\xff\xff\xff\xff\xff\xff\xfe", 9},
+      QCBOR_SUCCESS,
+      QCBOR_TYPE_INT64,
+      {-9223372036854775807, 0, 0}
+   },
+   {
+      "Largest representable in int64_t (INT64_MIN)",
       {"\x3B\x7f\xff\xff\xff\xff\xff\xff\xff", 9},
       QCBOR_SUCCESS,
       QCBOR_TYPE_INT64,
       {INT64_MIN, 0, 0}
    },
    {
+      "-9223372036854775809 First encoded as 65-bit neg",
+      {"\x3B\x80\x00\x00\x00\x00\x00\x00\x00", 9},
+      QCBOR_SUCCESS,
+      QCBOR_TYPE_65BIT_NEG_INT,
+      {0, 0, 0}
+   },
+
+   {
+      "-9223372036854777856 First float not representable as int64_t",
+      {"\x3B\x80\x00\x00\x00\x00\x00\x07\xFF", 9},
+      QCBOR_SUCCESS,
+      QCBOR_TYPE_DOUBLE,
+      {0, 0, -9223372036854777856.0}
+   },
+
+   {
       "18446742974197923840",
       {"\xFB\x43\xEF\xFF\xFF\xE0\x00\x00\x00", 9},
       QCBOR_SUCCESS,
       QCBOR_TYPE_UINT64,
       {0, 18446742974197923840ULL, 0}
    },
+
    {
-      "65-bit neg, too much precision",
-      {"\x3B\x80\x00\x00\x00\x00\x00\x00\x01", 9},
-      QCBOR_SUCCESS,
-      QCBOR_TYPE_65BIT_NEG_INT,
-      {0, 0x8000000000000001, 0}
-   },
-   {
-      "65-bit neg lots of precision",
-      {"\x3B\xff\xff\xff\xff\xff\xff\xf0\x00", 9},
+      "-18446744073709547522 65-bit neg lots of precision",
+      {"\x3B\xff\xff\xff\xff\xff\xff\xef\xff", 9},
       QCBOR_SUCCESS,
       QCBOR_TYPE_DOUBLE,
-      {0, 0, -18446744073709547521.0}
+      {0, 0, -18446744073709547522.0}
    },
+
+   {
+      "-18446744073709549568 Next to largest float encodable as 65-bit neg",
+      {"\x3B\xff\xff\xff\xff\xff\xff\xf7\xff", 9},
+      QCBOR_SUCCESS,
+      QCBOR_TYPE_DOUBLE,
+      {0, 0, -18446744073709549568.0}
+   },
+
+   {
+      "-18446744073709551616 Largest possible encoded 65-bit neg",
+      {"\x3B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+      QCBOR_SUCCESS,
+      QCBOR_TYPE_DOUBLE,
+      {0, 0, -18446744073709551616.0}
+   },
+   {
+      "-18446744073709551617 First value representable only as a tag 3 big num",
+      {"\xC3\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+#ifndef QCBOR_DISABLE_TAGS
+      QCBOR_ERR_UNEXPECTED_TYPE,
+#else
+      QCBOR_ERR_TAGS_DISABLED,
+#endif /* ! QCBOR_DISABLE_TAGS */
+      QCBOR_TYPE_NONE,
+      {0, 0, -0}
+   },
+   {
+      "-18446744073709555712 First whole integer that must be encoded as float in DCBOR",
+      {"\xFB\xC3\xF0\x00\x00\x00\x00\x00\x01", 9},
+      QCBOR_SUCCESS,
+      QCBOR_TYPE_DOUBLE,
+      {0, 0, -18446744073709555712.0}
+   },
+   
    {
       "65-bit neg very precise",
       {"\x3B\xff\xff\xff\xff\xff\xff\xf8\x00", 9},
       QCBOR_SUCCESS,
-      QCBOR_TYPE_DOUBLE,
-      {0, 0, -18446744073709549569.0}
+      QCBOR_TYPE_65BIT_NEG_INT,
+      {0, 18446744073709549568ULL, 0.0}
    },
    {
       "65-bit neg too precise",
@@ -10232,8 +10500,8 @@
       "65-bit neg, power of two",
       {"\x3B\x80\x00\x00\x00\x00\x00\x00\x00", 9},
       QCBOR_SUCCESS,
-      QCBOR_TYPE_DOUBLE,
-      {0, 0, -9223372036854775809.0}
+      QCBOR_TYPE_65BIT_NEG_INT,
+      {0, 9223372036854775808ULL, 0.0}
    },
    {
       "Zero",
@@ -10260,20 +10528,20 @@
 
 
 int32_t
-PreciseNumbersTest(void)
+PreciseNumbersDecodeTest(void)
 {
-   int                i;
+   unsigned           uTestIndex;
+   unsigned           uTestCount;
    QCBORError         uErr;
    QCBORItem          Item;
    QCBORDecodeContext DCtx;
    const struct PreciseNumberConversion *pTest;
 
-   const int count = (int)C_ARRAY_COUNT(PreciseNumberConversions, struct PreciseNumberConversion);
+   uTestCount = C_ARRAY_COUNT(PreciseNumberConversions, struct PreciseNumberConversion);
+   for(uTestIndex = 0; uTestIndex < uTestCount; uTestIndex++) {
+      pTest = &PreciseNumberConversions[uTestIndex];
 
-   for(i = 0; i < count; i++) {
-      pTest = &PreciseNumberConversions[i];
-
-      if(i == 11) {
+      if(uTestIndex == 18) {
          uErr = 99; // For break point only
       }
 
@@ -10284,11 +10552,11 @@
       uErr = QCBORDecode_GetError(&DCtx);
 
       if(uErr != pTest->uError) {
-         return i * 1000 + (int)uErr;
+         return MakeTestResultCode(uTestIndex, 1, uErr);
       }
 
       if(pTest->qcborType != Item.uDataType) {
-         return i * 1000 + 200;
+         return MakeTestResultCode(uTestIndex, 2, 0);
       }
 
       if(pTest->qcborType == QCBOR_TYPE_NONE) {
@@ -10298,25 +10566,25 @@
       switch(pTest->qcborType) {
          case QCBOR_TYPE_INT64:
             if(Item.val.int64 != pTest->number.int64) {
-               return i * 1000 + 300;
+               return MakeTestResultCode(uTestIndex, 3, 0);
             }
             break;
 
          case QCBOR_TYPE_UINT64:
-         case QCBOR_TYPE_65BIT_NEG_INT:
+         case QCBOR_TYPE_NEGBIGNUM:
             if(Item.val.uint64 != pTest->number.uint64) {
-               return i * 1000 + 400;
+               return MakeTestResultCode(uTestIndex, 4, 0);
             }
             break;
 
          case QCBOR_TYPE_DOUBLE:
             if(isnan(pTest->number.d)) {
                if(!isnan(Item.val.dfnum)) {
-                  return i * 1000 + 600;
+                  return MakeTestResultCode(uTestIndex, 5, 0);
                }
             } else {
                if(Item.val.dfnum != pTest->number.d) {
-                  return i * 1000 + 500;
+                  return MakeTestResultCode(uTestIndex, 6, 0);
                }
             }
             break;
diff --git a/test/qcbor_decode_tests.h b/test/qcbor_decode_tests.h
index a6d5837..31d9e48 100644
--- a/test/qcbor_decode_tests.h
+++ b/test/qcbor_decode_tests.h
@@ -185,7 +185,7 @@
 /*
  Parse some big numbers, positive and negative
  */
-int32_t BignumParseTest(void);
+int32_t BignumDecodeTest(void);
 
 
 /*
@@ -324,7 +324,7 @@
  */
 int32_t DecodeConformanceTests(void);
 
-int32_t PreciseNumbersTest(void);
+int32_t PreciseNumbersDecodeTest(void);
 
 int32_t ErrorHandlingTests(void);
 
diff --git a/test/qcbor_encode_tests.c b/test/qcbor_encode_tests.c
index 7645475..88251ac 100644
--- a/test/qcbor_encode_tests.c
+++ b/test/qcbor_encode_tests.c
@@ -128,6 +128,18 @@
 }
 
 
+static inline int32_t
+MakeTestResultCode(uint32_t   uTestCase,
+                   uint32_t   uTestNumber,
+                   QCBORError uErrorCode)
+{
+   uint32_t uCode = (uTestCase * 1000000) +
+                    (uTestNumber * 1000) +
+                    (uint32_t)uErrorCode;
+   return (int32_t)uCode;
+}
+
+
 // One big buffer that is used by all the tests to encode into
 // Putting it in uninitialized data is better than using a lot
 // of stack. The tests should run on small devices too.
@@ -276,10 +288,10 @@
 
 
 static const uint8_t spExpectedEncodedAll[] = {
- 0x98, 0x24, 0x66, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x32, 0xd8,
+ 0x98, 0x23, 0x66, 0x55, 0x49, 0x4e, 0x54, 0x36, 0x32, 0xd8,
  0x64, 0x1a, 0x05, 0x5d, 0x23, 0x15, 0x65, 0x49, 0x4e, 0x54,
  0x36, 0x34, 0xd8, 0x4c, 0x1b, 0x00, 0x00, 0x00, 0x12, 0x16,
- 0xaf, 0x2b, 0x15, 0x00, 0x38, 0x2b, 0x20, 0xa4, 0x63, 0x4c, 0x42,
+ 0xaf, 0x2b, 0x15, 0x00, 0x38, 0x2b, 0xa4, 0x63, 0x4c, 0x42,
  0x4c, 0x18, 0x4d, 0x23, 0x18, 0x58, 0x78, 0x1a, 0x4e, 0x45,
  0x47, 0x4c, 0x42, 0x4c, 0x54, 0x48, 0x41, 0x54, 0x20, 0x49,
  0x53, 0x20, 0x4b, 0x49, 0x4e, 0x44, 0x20, 0x4f, 0x46, 0x20,
@@ -476,11 +488,11 @@
  0x71, 0x47, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x20, 0x69, 0x73,
  0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x61, 0x6e, 0xf5, 0x19,
  0x10, 0x41, 0xf5, 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xC3, 0x49, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0xC3, 0x49, 0x01, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0x63, 0x42, 0x4E, 0x2B,
  0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x18, 0x40, 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x63, 0x42, 0x4E, 0x2D, 0xC3, 0x49,
+ 0x01, 0x18, 0x40, 0xC2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x63, 0x42, 0x4E, 0x2D, 0xC3, 0x49,
  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
  0x3F, 0xC3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00
@@ -523,7 +535,6 @@
    QCBOREncode_AddInt64(pECtx, 77689989909);
    QCBOREncode_AddUInt64(pECtx, 0);
    QCBOREncode_AddInt64(pECtx, -44);
-   QCBOREncode_AddNegativeUInt64(pECtx, 0);
 
    /* ints that go in maps */
    QCBOREncode_OpenMap(pECtx);
@@ -672,7 +683,7 @@
    QCBOREncode_CloseMap(pECtx);
 
    /* Big numbers */
-   static const uint8_t pBignum[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+   static const uint8_t pBignum[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
    const UsefulBufC BIGNUM = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pBignum);
    QCBOREncode_AddPositiveBignum(pECtx, BIGNUM);
    QCBOREncode_AddNegativeBignum(pECtx, BIGNUM);
@@ -736,13 +747,6 @@
 #else
    uExpectedErr = QCBOR_SUCCESS;
 #endif
-   /* Test the QCBOR_ERR_NOT_ALLOWED error codes */
-   QCBOREncode_Init(&ECtx, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
-   QCBOREncode_AddNegativeUInt64(&ECtx, 1);
-   if(QCBOREncode_Finish(&ECtx, &Enc) != uExpectedErr) {
-      nReturn = -21;
-      goto Done;
-   }
 
 
 #if !defined(QCBOR_DISABLE_ENCODE_USAGE_GUARDS) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
@@ -944,6 +948,131 @@
    return(nReturn);
 }
 
+struct BigNumEncodeTest {
+   const char *szDescription;
+   UsefulBufC  BigNum;
+   /* Expect all to succeed; no special error codes needed */
+   UsefulBufC  PositiveNoPreferred;
+   UsefulBufC  PositivePreferred;
+   UsefulBufC  NegativeNoPreferred;
+   UsefulBufC  NegativePreferred;
+};
+
+struct BigNumEncodeTest BigNumEncodeTestCases[] = {
+   {
+      "2^96 -1 or 79228162514264337593543950335 pos and neg with leading zeros",
+      {"\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 15},
+      {"\xC2\x4F\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 17},
+      {"\xC2\x4C\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 14},
+      {"\xC3\x4C\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe", 14},
+      {"\xC3\x4C\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe", 14},
+   },
+
+   {
+      "2^64+1 or 18446744073709551617 pos and neg)",
+      {"\x01\x00\x00\x00\x00\x00\x00\x00\x01", 9},
+      {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x01", 11},
+      {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x01", 11},
+      {"\xC3\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+      {"\xC3\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+   },
+   {
+      "2^64 or 18446744073709551616 pos and neg)",
+      {"\x01\x00\x00\x00\x0000\x00\x00\x00", 9},
+      {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+      {"\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+      {"\xC3\x48\xff\xff\xff\xff\xff\xff\xff\xff", 10},
+      {"\x3B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+   },
+   {
+      "2^64 - 1 or 18446744073709551615 pos and neg",
+      {"\xff\xff\xff\xff\xff\xff\xff\xff", 8},
+      {"\xC2\x48\xff\xff\xff\xff\xff\xff\xff\xff", 10},
+      {"\x1B\xff\xff\xff\xff\xff\xff\xff\xff", 9},
+      {"\xC3\x48\xff\xff\xff\xff\xff\xff\xff\xfe", 10},
+      {"\x3B\xff\xff\xff\xff\xff\xff\xff\xfe", 9},
+   },
+   {
+      "1 and -1",
+      {"\x01", 1},
+      {"\xC2\x41\x01", 3},
+      {"\x01", 1},
+      {"\xC3\x41\x00", 3},
+      {"\x20", 1},
+   },
+   {
+      "0 and error",
+      {"\x00", 1},
+      {"\xC2\x41\x00", 3},
+      {"\x00", 1},
+      NULLUsefulBufC,
+      NULLUsefulBufC,
+   },
+   {
+      "leading zeros -- 0 and error",
+      {"\x00\x00\x00\x00", 4},
+      {"\xC2\x44\x00\x00\x00\x00", 6},
+      {"\x00", 1},
+      NULLUsefulBufC,
+      NULLUsefulBufC,
+   }
+
+};
+
+
+int32_t BigNumEncodeTests(void)
+{
+   unsigned           uTestIndex;
+   unsigned           uTestCount;
+   QCBOREncodeContext Enc;
+   UsefulBufC         B;
+   const struct BigNumEncodeTest *pTest;
+
+   uTestCount = (int)C_ARRAY_COUNT(BigNumEncodeTestCases, struct BigNumEncodeTest);
+
+   for(uTestIndex = 0; uTestIndex < uTestCount; uTestIndex++) {
+      pTest = &BigNumEncodeTestCases[uTestIndex];
+
+      if(uTestIndex == 3) {
+         B.len = 0; /* Line of code so a break point can be set. */
+      }
+
+      QCBOREncode_Init(&Enc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+      QCBOREncode_AddTPositiveBignumNoPreferred(&Enc, QCBOR_ENCODE_AS_TAG, pTest->BigNum);
+      QCBOREncode_Finish(&Enc, &B);
+      if(UsefulBuf_Compare(B, pTest->PositiveNoPreferred)) {
+         return MakeTestResultCode(uTestIndex, 1, QCBOR_SUCCESS);
+      }
+
+      QCBOREncode_Init(&Enc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+      QCBOREncode_AddPositiveBignum (&Enc, pTest->BigNum);
+      QCBOREncode_Finish(&Enc, &B);
+      if(UsefulBuf_Compare(B, pTest->PositivePreferred)) {
+         return MakeTestResultCode(uTestIndex, 2, QCBOR_SUCCESS);
+      }
+
+      if(!UsefulBuf_IsNULLC(pTest->NegativeNoPreferred)){
+         QCBOREncode_Init(&Enc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+         QCBOREncode_AddTNegativeBignumNoPreferred(&Enc, QCBOR_ENCODE_AS_TAG, pTest->BigNum);
+         QCBOREncode_Finish(&Enc, &B);
+         if(UsefulBuf_Compare(B, pTest->NegativeNoPreferred)) {
+            return MakeTestResultCode(uTestIndex, 3, QCBOR_SUCCESS);
+         }
+      }
+
+      if(!UsefulBuf_IsNULLC(pTest->NegativePreferred)){
+         QCBOREncode_Init(&Enc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+         QCBOREncode_AddNegativeBignum(&Enc, pTest->BigNum);
+         QCBOREncode_Finish(&Enc, &B);
+         if(UsefulBuf_Compare(B, pTest->NegativePreferred)) {
+            return MakeTestResultCode(uTestIndex, 4, QCBOR_SUCCESS);
+         }
+      }
+   }
+
+   return 0;
+}
+
 
 /*
  85                  # array(5)
@@ -2765,12 +2894,12 @@
    0x07, 0x08, 0x09, 0x10, 0xC4, 0x82, 0x1B, 0x7F,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3,
    0x4A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-   0x08, 0x09, 0x10, 0xC5, 0x82, 0x19, 0x01, 0x2C,
+   0x08, 0x09, 0x0F, 0xC5, 0x82, 0x19, 0x01, 0x2C,
    0x18, 0x64, 0xC5, 0x82, 0x33, 0xC2, 0x4A, 0x01,
    0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
    0x10, 0xC5, 0x82, 0x3B, 0x7F, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x4A, 0x01, 0x02,
-   0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10};
+   0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0F};
 
 
 /*
@@ -2809,10 +2938,10 @@
    0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0xC4, 0x82,
    0x1B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xC3, 0x4A, 0x01, 0x02, 0x03, 0x04, 0x05,
-   0x06, 0x07, 0x08, 0x09, 0x10, 0x19, 0x01, 0xF4,
+   0x06, 0x07, 0x08, 0x09, 0x0F, 0x19, 0x01, 0xF4,
    0xC4, 0x82, 0x1B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xC3, 0x4A, 0x01, 0x02, 0x03,
-   0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x69,
+   0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0F, 0x69,
    0x62, 0x69, 0x67, 0x20, 0x66, 0x6C, 0x6F, 0x61,
    0x74, 0xC5, 0x82, 0x19, 0x01, 0x2C, 0x18, 0x64,
    0x19, 0x02, 0x58, 0xC5, 0x82, 0x19, 0x01, 0x2C,
@@ -2829,18 +2958,153 @@
    0x20, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76,
    0x65, 0xC5, 0x82, 0x3B, 0x7F, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x4A, 0x01, 0x02,
-   0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+   0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0F,
    0x19, 0x03, 0x20, 0xC5, 0x82, 0x3B, 0x7F, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x4A,
    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
-   0x09, 0x10
+   0x09, 0x0F
 };
 
 
-int32_t ExponentAndMantissaEncodeTests(void)
+struct EAMEncodeTest {
+   const char *szDescription;
+   int64_t     nExponent;
+   UsefulBufC  BigNumMantissa;
+   int64_t     nMantissa;
+   bool        bSign;
+   bool        bv1Mode;
+   enum {EAM_Any, EAM_Pref, EAM_CDE} eSerialization;
+   // TODO: add tag requirement
+
+   /* Only testing successes (right?) */
+   UsefulBufC  BigFloat;
+   UsefulBufC  DecFrac;
+   UsefulBufC  BigFloatBig;
+   UsefulBufC  DecFracBig;
+};
+
+struct EAMEncodeTest EET[] = {
+   { "basic",
+      -1,
+      NULLUsefulBufC,
+      3,
+      false,
+      false,
+      EAM_Pref,
+
+      {"\xC5\x82\x20\x03", 4},
+      {"\xC4\x82\x20\x03", 4},
+      NULLUsefulBufC,
+      NULLUsefulBufC
+   },
+
+   { "bignum gets preferred",
+      -1,
+      {"\x00\x03",2},
+      0,
+      false,
+      false,
+      EAM_Pref,
+
+      NULLUsefulBufC,
+      NULLUsefulBufC,
+      {"\xC5\x82\x20\x03", 4},
+      {"\xC4\x82\x20\x03", 4},
+   }
+
+   // TODO: add more test cases, including converting some of the already-existing
+};
+
+
+
+static void
+EAMTestSetup(const struct EAMEncodeTest *pTest, QCBOREncodeContext *pEnc)
+{
+   QCBOREncode_Init(pEnc, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
+   if(pTest->bv1Mode) {
+      QCBOREncode_Setv1Compatibility(pEnc);
+   }
+
+   switch(pTest->eSerialization) {
+      case EAM_Pref:
+         QCBOREncode_SerializationPreferred(pEnc);
+         break;
+      case EAM_CDE:
+         QCBOREncode_SerializationCDE(pEnc);
+         break;
+
+      default:
+         break;
+   }
+}
+
+
+
+int32_t
+ExponentAndMantissaEncodeTests(void)
 {
    QCBOREncodeContext EC;
    UsefulBufC         EncodedExponentAndMantissa;
+   int                nIndex;
+   QCBORError         uErr;
+
+   const int nNumberOfTests = C_ARRAY_COUNT(EET, struct EAMEncodeTest);
+
+   for(nIndex = 0; nIndex < nNumberOfTests; nIndex++) {
+      struct EAMEncodeTest *pTest = &EET[nIndex];
+
+
+      if(UsefulBuf_IsNULLC(pTest->BigNumMantissa)) {
+         EAMTestSetup(pTest, &EC);
+
+         QCBOREncode_AddDecimalFraction(&EC, pTest->nMantissa, pTest->nExponent);
+         uErr = QCBOREncode_Finish(&EC, &EncodedExponentAndMantissa);
+         if(uErr) {
+            return MakeTestResultCode((uint32_t)nIndex, 1, uErr);
+         }
+
+         if(UsefulBuf_Compare(EncodedExponentAndMantissa, pTest->DecFrac)) {
+            return MakeTestResultCode((uint32_t)nIndex, 2, 0);
+         }
+
+         EAMTestSetup(pTest, &EC);
+         QCBOREncode_AddBigFloat(&EC, pTest->nMantissa, pTest->nExponent);
+         uErr = QCBOREncode_Finish(&EC, &EncodedExponentAndMantissa);
+         if(uErr) {
+            return MakeTestResultCode((uint32_t)nIndex, 11, uErr);
+         }
+
+         if(UsefulBuf_Compare(EncodedExponentAndMantissa, pTest->BigFloat)) {
+            return MakeTestResultCode((uint32_t)nIndex, 12, 0);
+         }
+
+      } else {
+         EAMTestSetup(pTest, &EC);
+
+         QCBOREncode_AddDecimalFractionBigNum(&EC, pTest->BigNumMantissa, pTest->bSign, pTest->nExponent);
+         uErr = QCBOREncode_Finish(&EC, &EncodedExponentAndMantissa);
+         if(uErr) {
+            return MakeTestResultCode((uint32_t)nIndex, 11, uErr);
+         }
+
+         if(UsefulBuf_Compare(EncodedExponentAndMantissa, pTest->DecFracBig)) {
+            return MakeTestResultCode((uint32_t)nIndex, 12, 0);
+         }
+
+         EAMTestSetup(pTest, &EC);
+
+         QCBOREncode_AddBigFloatBigNum(&EC, pTest->BigNumMantissa, pTest->bSign, pTest->nExponent);
+         uErr = QCBOREncode_Finish(&EC, &EncodedExponentAndMantissa);
+         if(uErr) {
+            return MakeTestResultCode((uint32_t)nIndex, 11, uErr);
+         }
+
+         if(UsefulBuf_Compare(EncodedExponentAndMantissa, pTest->BigFloatBig)) {
+            return MakeTestResultCode((uint32_t)nIndex, 12, 0);
+         }
+      }
+   }
+
 
    // Constant for the big number used in all the tests.
    static const uint8_t spBigNum[] = {0x01, 0x02, 0x03, 0x04, 0x05,
@@ -2861,9 +3125,11 @@
       return -2;
    }
 
+   struct UBCompareDiagnostic Foo;
+
    int nReturn = UsefulBuf_CompareWithDiagnostic(EncodedExponentAndMantissa,
                                                  UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedExponentAndMantissaArray),
-                                                 NULL);
+                                                 &Foo);
    if(nReturn) {
       return nReturn;
    }
@@ -2933,9 +3199,7 @@
       return -3;
    }
 
-
    struct UBCompareDiagnostic Diag;
-
    nReturn = UsefulBuf_CompareWithDiagnostic(EncodedExponentAndMantissa,
                                              UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedExponentAndMantissaMap),
                                              &Diag);
@@ -3650,6 +3914,9 @@
    QCBOREncode_AddInt64ToMap(&EC, "a", 1);
    QCBOREncode_AddDoubleToMap(&EC, "x", 2.0);
    QCBOREncode_AddDoubleToMap(&EC, "r", 3.4028234663852886E+38);
+   QCBOREncode_AddDoubleToMap(&EC, "d1", -18446744073709549568.0);
+   QCBOREncode_AddDoubleToMap(&EC, "d2", -18446744073709551616.0);
+   QCBOREncode_AddDoubleToMap(&EC, "d3", -18446744073709555712.0);
    QCBOREncode_AddDoubleToMap(&EC, "b", NAN);
 
    QCBOREncode_CloseMap(&EC);
@@ -3657,9 +3924,7 @@
    QCBOREncode_Finish(&EC, &Encoded);
 
    static const uint8_t spExpecteddCBOR[] = {
-      0xA5, 0x61, 0x61, 0x01, 0x61, 0x62, 0xF9, 0x7E,
-      0x00, 0x61, 0x6B, 0x01, 0x61, 0x72, 0xFA, 0x7F,
-      0x7F, 0xFF, 0xFF, 0x61, 0x78, 0x02};
+      0xA8, 0x61, 0x61, 0x01, 0x61, 0x62, 0xF9, 0x7E, 0x00, 0x61, 0x6B, 0x01, 0x61, 0x72, 0xFA, 0x7F, 0x7F, 0xFF, 0xFF, 0x61, 0x78, 0x02, 0x62, 0x64, 0x31, 0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0x62, 0x64, 0x32, 0x3B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x62, 0x64, 0x33, 0xFB, 0xC3, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
 
    if(UsefulBuf_Compare(UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpecteddCBOR),
                         Encoded)) {
@@ -3704,13 +3969,6 @@
       return 102;
    }
 
-   /* 65-bit negative integers */
-   QCBOREncode_Init(&EC, UsefulBuf_FROM_BYTE_ARRAY(spBigBuf));
-   QCBOREncode_SerializationdCBOR(&EC);
-   QCBOREncode_AddNegativeUInt64(&EC, 1);
-   if(QCBOREncode_GetErrorState(&EC) != uExpectedErr) {
-      return 103;
-   }
 
    /* Improvement: when indefinite length string encoding is supported
     * test it here too. */
diff --git a/test/qcbor_encode_tests.h b/test/qcbor_encode_tests.h
index 5e2f978..1be5635 100644
--- a/test/qcbor_encode_tests.h
+++ b/test/qcbor_encode_tests.h
@@ -94,9 +94,9 @@
 
 
 /*
- Encodes a goodly number of floats and doubles and checks encoding is right
+ * Big number encoding tests.
  */
-int32_t FloatValuesTest1(void);
+int32_t BigNumEncodeTests(void);
 
 
 /*
diff --git a/test/run_tests.c b/test/run_tests.c
index 3fe4905..682d5e1 100644
--- a/test/run_tests.c
+++ b/test/run_tests.c
@@ -67,6 +67,7 @@
 
 
 static test_entry s_tests[] = {
+   TEST_ENTRY(BigNumEncodeTests),
 #ifndef QCBOR_DISABLE_DECODE_CONFORMANCE
    TEST_ENTRY(DecodeConformanceTests),
 #endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE */
@@ -81,8 +82,6 @@
 #endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
    TEST_ENTRY(SpiffyDateDecodeTest),
 #endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
-   TEST_ENTRY(ErrorHandlingTests),
-   TEST_ENTRY(OpenCloseBytesTest),
    TEST_ENTRY(EnterBstrTest),
    TEST_ENTRY(IntegerConvertTest),
    TEST_ENTRY(EnterMapTest),
@@ -114,7 +113,7 @@
 #endif /* ! QCBOR_DISABLE_NON_INTEGER_LABELS */
    TEST_ENTRY(BasicEncodeTest),
    TEST_ENTRY(NestedMapTest),
-   TEST_ENTRY(BignumParseTest),
+   TEST_ENTRY(BignumDecodeTest),
 
 #ifndef QCBOR_DISABLE_TAGS
    TEST_ENTRY(OptTagParseTest),
@@ -142,7 +141,7 @@
 #ifndef QCBOR_DISABLE_PREFERRED_FLOAT
    TEST_ENTRY(HalfPrecisionAgainstRFCCodeTest),
    TEST_ENTRY(FloatValuesTests),
-   TEST_ENTRY(PreciseNumbersTest),
+   TEST_ENTRY(PreciseNumbersDecodeTest),
 #endif /* QCBOR_DISABLE_PREFERRED_FLOAT */
    TEST_ENTRY(GeneralFloatEncodeTests),
    TEST_ENTRY(GeneralFloatDecodeTests),