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/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index 1441ade..e51ebc6 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -31,7 +31,6 @@
  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * ========================================================================= */
 
-
 #ifndef qcbor_common_h
 #define qcbor_common_h
 
@@ -556,6 +555,9 @@
    /** Conformance checking requested, preferred serialization disabled, float in the input. */
    QCBOR_ERR_CANT_CHECK_FLOAT_CONFORMANCE = 86,
 
+   /* Can't output a negative zero big num */
+   QCBOR_ERR_NO_NEGATIVE_ZERO = 87,
+
    /** A range of error codes that can be made use of by the
     * caller. QCBOR internally does nothing with these except notice
     * that they are not QCBOR_SUCCESS. See QCBORDecode_SetError(). */
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index c2da5a2..f2f4a4b 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -247,7 +247,7 @@
 
 /** Type for an integer that decoded either between @c INT64_MIN and
  *  @c INT32_MIN or @c INT32_MAX and @c INT64_MAX. Data is in member
- *  @c val.int64. */
+ *  @c val.int64. See also \ref QCBOR_TYPE_65BIT_NEG_INT */
 #define QCBOR_TYPE_INT64          2
 
 /** Type for an integer that decoded to a more than @c INT64_MAX and
@@ -269,11 +269,19 @@
 #define QCBOR_TYPE_TEXT_STRING    7
 
 /** Type for a positive big number. Data is in @c val.bignum, a
- *  pointer and a length. */
+ *  pointer and a length. See QCBORDecode_BignumPreferred(). */
 #define QCBOR_TYPE_POSBIGNUM      9
 
 /** Type for a negative big number. Data is in @c val.bignum, a
- *  pointer and a length. */
+ *  pointer and a length. Type 1 integers in the range of [-2^64,
+ *  -2^63 - 1] are returned in this type.  1 MUST be subtracted from
+ *  what is returned to get the actual value. This is because of the
+ *  way CBOR negative numbers are represented. QCBOR doesn't do this
+ *  because it can't be done without storage allocation and QCBOR
+ *  avoids storage allocation for the most part.  For example, if 1 is
+ *  subtraced from a negative big number that is the two bytes 0xff
+ *  0xff, the result would be 0x01 0x00 0x00, one byte longer than
+ *  what was received. See QCBORDecode_BignumPreferred(). */
 #define QCBOR_TYPE_NEGBIGNUM     10
 
 /** Type for [RFC 3339] (https://tools.ietf.org/html/rfc3339) date
@@ -504,6 +512,7 @@
       /** The value for @c uDataType @ref QCBOR_TYPE_POSBIGNUM and
        * @ref QCBOR_TYPE_NEGBIGNUM.  */
       UsefulBufC  bigNum;
+
       /** See @ref QCBOR_TYPE_UKNOWN_SIMPLE */
       uint8_t     uSimple;
 #ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
@@ -1373,6 +1382,68 @@
 QCBORDecode_SetError(QCBORDecodeContext *pCtx, QCBORError uError);
 
 
+/**
+ * @brief Decode a preferred serialization big number.
+ *
+ * @param[in] Item    The number to process.
+ * @param[in] BigNumBuf  The buffer to output to.
+ * @param[out] pBigNum   The resulting big number.
+ * @param[out] pIsNegative  The sign of the resulting big number.
+ *
+ * This exists to process an item that is expected to be a big number
+ * encoded with preferred serialization.  This processing is not part
+ * of the main decoding because of the number of CBOR types it
+ * involves, because it needs a buffer to output to, and to keep code
+ * size of the core decoding small.
+ *
+ * This can also be used to do the subtraction of 1 for negative big
+ * numbers even if preferred serialization of big numbers is not in
+ * use.
+ *
+ * This works on all CBOR type 0 and 1 integers and all tag 2 and 3
+ * big numbers.  In terms of QCBOR types, this works on
+ * \ref QCBOR_TYPE_INT64, \ref QCBOR_TYPE_UINT64,
+ * \ref QCBOR_TYPE_65BIT_NEG, \ref QCBOR_TYPE_POSBIGNUM and
+ * \ref QCBOR_TYPE_NEGBIGNUM.
+ *
+ * This always returns the result as a big number. The integer types 0
+ * and 1 are converted. Leading zeros are removed. The value 0 is
+ * always returned as a one-byte big number with the value 0x00.
+
+ * If \c BigNumBuf is too small, \c pBigNum.ptr will be \c NULL and \c
+ * pBigNum.len reports the required length. The size of \c BigNumBuf
+ * might have to be one larger than the size of the tag 2 or 3 being
+ * decode because of two cases. In CBOR the value of a tag 3 big
+ * number is -n - 1. The subtraction of one might have a carry.  For
+ * example, an encoded tag 3 that is 0xff, is returned here as 0x01
+ * 0x00.  The other case is a empty tag 2 which is returned as a
+ * one-byte big number with the value 0x00.  (This is the only place
+ * in all of RFC 8949 except for indefinite length strings where the
+ * encoded buffer off the wire can't be returned directly, the only
+ * place some storage allocation is required.)
+ *
+ * This is the decode-side implementation of preferred serialization
+ * of big numbers described in section 3.4.3 of RFC 8949. It
+ * implements the decode-side unification of big numbers and regular
+ * integers.
+ *
+ * This can also be used if you happen to want type 0 and type 1
+ * integers converted to big numbers.
+ *
+ * If QCBOR is being used in an environment with a full big number
+ * library, it may be better (less object code) to use the big number
+ * library than this, particularly to subtract one for tag 3.
+ *
+ * Finally, the object code for this function is suprisingly large,
+ * almost 1KB. This is due to the number of CBOR data types, and the
+ * big number math required to subtract one and the buffer sizing
+ * issue it brings.
+ */
+QCBORError
+QCBORDecode_BignumPreferred(const QCBORItem Item,
+                            UsefulBuf       BigNumBuf,
+                            UsefulBufC     *pBigNum,
+                            bool           *pIsNegative);
 
 
 /**
diff --git a/inc/qcbor/qcbor_encode.h b/inc/qcbor/qcbor_encode.h
index a9aebff..96a91e1 100644
--- a/inc/qcbor/qcbor_encode.h
+++ b/inc/qcbor/qcbor_encode.h
@@ -471,6 +471,36 @@
 QCBOREncode_Init(QCBOREncodeContext *pCtx, UsefulBuf Storage);
 
 
+/* TODO: allow mix-and-match serialization behaviors
+ - Sort maps
+ - Reduce floats to integers
+ - Restrict non-preferred big nums
+ - Restrict indefinite-length strings
+ - Restrict indefinite-length maps and arrays
+ - Restrict simple values
+ - Dup detection
+ - Restrict NaN payload
+
+ Easy to do by changing uMode into a bit map.
+
+ Same for decode conformance check.
+ */
+
+
+/**
+ * @brief Select preferred serialization mode.
+ *
+ * @param[in] pCtx   The encoding context for mode set.
+ *
+ * This resests to the default serialization behaviors, that
+ * allows non-preferred serialization methods to be called,
+ * doesn't sort maps, and doesn't reduce whole-number floats
+ * to integer.
+ */
+static void
+QCBOREncode_SerializationAny(QCBOREncodeContext *pCtx);
+
+
 /**
  * @brief Select preferred serialization mode.
  *
@@ -555,8 +585,6 @@
  * This mode forces all NaNs to the half-precision queit NaN. Also see
  * QCBOREncode_Allow().
  *
- * dCBOR also disallows 65-bit negative integers.
- *
  * dCBOR disallows use of any simple type other than true, false and
  * NULL. In particular it disallows use of "undef" produced by
  * QCBOREncode_AddUndef().
@@ -578,13 +606,8 @@
  *  QCBOREncode_AddSingleleNoPreferred. */
 #define QCBOR_ENCODE_ALLOW_NAN_PAYLOAD 0x01
 
-/** Bit flag to be passed to QCBOREncode_Allow() to allow use of
- *  QCBOREncode_AddNegativeUInt64(). */
-#define QCBOR_ENCODE_ALLOW_65_BIG_NEG  0x02
-
 /** Bit flag to be passed to QCBOREncode_Allow() output of less
- *  interoperable values. See @ref QCBOR_ENCODE_ALLOW_NAN_PAYLOAD, and
- *  @ref QCBOR_ENCODE_ALLOW_65_BIG_NEG. */
+ *  interoperable values. See @ref QCBOR_ENCODE_ALLOW_NAN_PAYLOAD */
 #define QCBOR_ENCODE_ALLOW_ALL         0xFF
 
 
@@ -608,6 +631,12 @@
 QCBOREncode_Allow(QCBOREncodeContext *pCtx, uint8_t uAllow);
 
 
+/*
+ * QCBOR_ENCODE_CONFIG_V1_COMPAT
+ */
+static void
+QCBOREncode_Setv1Compatibility(QCBOREncodeContext *pCtx);
+
 
 /**
  * @brief  Add a signed 64-bit integer to the encoded output.
@@ -617,14 +646,14 @@
  *
  * The integer will be encoded and added to the CBOR output.
  *
- * This function figures out the size and the sign and encodes in the
- * correct minimal CBOR. Specifically, it will select CBOR major type
+ * This function figures out the size and the sign and encodes using
+ * CBOR preferred serialization. Specifically, it will select CBOR major type
  * 0 or 1 based on sign and will encode to 1, 2, 4 or 8 bytes
  * depending on the value of the integer. Values less than 24
  * effectively encode to one byte because they are encoded in with the
- * CBOR major type.  This is a neat and efficient characteristic of
+ * CBOR major type. This is a neat and efficient characteristic of
  * CBOR that can be taken advantage of when designing CBOR-based
- * protocols. If integers like tags can be kept between -23 and 23
+ * protocols. If integers can be kept between -23 and 23
  * they will be encoded in one byte including the major type.
  *
  * If you pass a smaller int, say an @c int16_t or a small value, say
@@ -666,7 +695,7 @@
  * @param[in] pCtx  The encoding context to add the integer to.
  * @param[in] uNum  The integer to add.
  *
- * The integer will be encoded and added to the CBOR output.
+ * The integer is encoded and added to the CBOR output.
  *
  * The only reason so use this function is for integers larger than
  * @c INT64_MAX and smaller than @c UINT64_MAX. Otherwise
@@ -696,10 +725,6 @@
  * the sign as a bit) which is possible because CBOR happens to
  * support such integers.
  *
- * Because use of this is discouraged. It must be explicitly allowed
- * by passing @ref QCBOR_ENCODE_ALLOW_65_BIG_NEG to a call to
- * QCBOREncode_Allow().
- *
  * The actual value encoded is -uNum - 1. That is, give 0 for uNum to
  * transmit -1, give 1 to transmit -2 and give UINT64_MAX to transmit
  * -UINT64_MAX-1 (18446744073709551616). The interface is odd like
@@ -707,9 +732,9 @@
  * QCBOR (making this a complete CBOR implementation).
  *
  * The most negative value QCBOREncode_AddInt64() can encode is
- * -9223372036854775808 which is -2^63 or negative 0x800000000000.
+ * -9223372036854775808 which is -(2^63) or negative 0x800000000000.
  * This can encode from -9223372036854775809 to -18446744073709551616
- * or -2^63 - 1 to -2^64. Note that it is not possible to represent
+ * or -(2^63 +1)  to -(2^64). Note that it is not possible to represent
  * positive or negative 18446744073709551616 in any standard C data
  * type.
  *
@@ -717,14 +742,14 @@
  * @ref QCBOR_TYPE_INT64.  Integers in the range of -9223372036854775809
  * to -18446744073709551616 are returned as @ref QCBOR_TYPE_65BIT_NEG_INT.
  *
- * WARNING: some CBOR decoders will be unable to decode -2^63 - 1 to
- * -2^64.  Also, most CPUs do not have registers that can represent
+ * WARNING: some CBOR decoders will be unable to decode -(2^63 + 1) to
+ * -(2^64).  Also, most CPUs do not have registers that can represent
  * this range.  If you need 65-bit negative integers, you likely need
  * negative 66, 67 and 68-bit negative integers so it is likely better
  * to use CBOR big numbers where you can have any number of bits. See
  * QCBOREncode_AddTNegativeBignum() and @ref Serialization.
  */
-void
+static void
 QCBOREncode_AddNegativeUInt64(QCBOREncodeContext *pCtx, uint64_t uNum);
 
 static void
@@ -733,6 +758,7 @@
 static void
 QCBOREncode_AddNegativeUInt64ToMapN(QCBOREncodeContext *pCtx, int64_t nLabel, uint64_t uNum);
 
+
 /**
  * @brief  Add a UTF-8 text string to the encoded output.
  *
@@ -806,39 +832,42 @@
  * @param[in] pCtx  The encoding context to add the double to.
  * @param[in] dNum  The double-precision number to add.
  *
- * This encodes and outputs a floating-point number. CBOR major type 7
- * is used.
- *
- * This implements preferred serialization, selectively encoding the
- * double-precision floating-point number as either double-precision,
+ * This encodes using preferred serialization, selectively encoding
+ * the input floating-point number as either double-precision,
  * single-precision or half-precision. Infinity, NaN and 0 are always
- * encoded as half-precision. If no precision will be lost in the
- * conversion to half-precision, then it will be converted and
- * encoded. If not and no precision will be lost in conversion to
- * single-precision, then it will be converted and encoded. If not,
- * then no conversion is performed, and it encoded as a
- * double-precision.
+ * encoded as half-precision. The reduction to single-precision or
+ * half-precision is only performed if there is no loss or precision.
  *
  * Half-precision floating-point numbers take up 2 bytes, half that of
- * single-precision, one quarter of double-precision
+ * single-precision, one quarter of double-precision. This can reduce
+ * the size of encoded output a lot, especially if the values 0,
+ * infinity and NaN occur frequently.
  *
- * This automatically reduces the size of encoded CBOR, maybe even by
- * four if most of values are 0, infinity or NaN.
+ * QCBOR decoding returns double-precision reversing this reduction.
  *
- * When decoded, QCBOR will usually return these values as
- * double-precision.
- *
- * It is possible to disable this preferred serialization when compiling
- * QCBOR. In that case, this functions the same as
- * QCBOREncode_AddDoubleNoPreferred().
+ * Normally this outputs only CBOR major type 7.  If
+ * QCBOREncode_SerializationdCBOR() is called to enter dCBOR mode,
+ * floating-point inputs that are whole integers are further reduced
+ * to CBOR type 0 and 1. This is a unification of the floating-point
+ * and integer number spaces such that there is only one encoding of
+ * any numeric value. Note that this will result in the whole integers
+ * from -(2^63+1) to -(2^64) being encode as CBOR major type 1 which
+ * can't be directly decoded into an int64_t or uint64_t. See
+ * QCBORDecode_GetNumberConvertPrecisely(), a good method to use to
+ * decode dCBOR.
  *
  * Error handling is the same as QCBOREncode_AddInt64().
  *
+ * It is possible that preferred serialization is disabled when the
+ * QCBOR library was built. In that case, this functions the same as
+ * QCBOREncode_AddDoubleNoPreferred().
+ *
  * See also QCBOREncode_AddDoubleNoPreferred(), QCBOREncode_AddFloat()
  * and QCBOREncode_AddFloatNoPreferred() and @ref Floating-Point.
  *
  * By default, this will error out on an attempt to encode a NaN with
- * a payload. See QCBOREncode_Allow() and @ref QCBOR_ENCODE_ALLOW_NAN_PAYLOAD.
+ * a payload. See QCBOREncode_Allow() and @ref
+ * QCBOR_ENCODE_ALLOW_NAN_PAYLOAD.
  * If preferred serialization is disabled at compliation, this check for
  * for NaN payloads is disabled.
  */
@@ -859,7 +888,7 @@
  * @param[in] fNum  The single-precision number to add.
  *
  * This is identical to QCBOREncode_AddDouble() except the input is
- * single-precision.
+ * single-precision. It also supports dCBOR.
  *
  * See also QCBOREncode_AddDouble(), QCBOREncode_AddDoubleNoPreferred(),
  * and QCBOREncode_AddFloatNoPreferred() and @ref Floating-Point.
@@ -1185,80 +1214,141 @@
 
 
 /**
- * @brief Add a positive big number to the encoded output.
+ * @brief Add a positive big number using preferred serialization.
  *
  * @param[in] pCtx             The encoding context to add the big number to.
  * @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or
  *                             @ref QCBOR_ENCODE_AS_BORROWED.
- * @param[in] Bytes            Pointer and length of the big number.
+ * @param[in] BigNumber            Pointer and length of the big number.
  *
- * Big numbers are integers larger than 64-bits. Their format is
- * described in [RFC 8949] (https://tools.ietf.org/html/rfc8949).
+ * @c BigNumber makes up an aribtrary precision integer in
+ * network/big-endian byte order.  The first byte is the most
+ * significant.
  *
- * It is output as CBOR major type 2, a binary string, with tag
- * @ref CBOR_TAG_POS_BIGNUM indicating the binary string is a positive
- * big number.
+ * If the value in @c BigNumber is greater than @c UINT64_MAX, this
+ * will be encoded as the CBOR tag @ref CBOR_TAG_POS_BIGNUM. If less
+ * then, it will be output as a type 0 integer. This is as required
+ * for preferred serialization described in RFC 8949 section 3.4.3.
  *
- * Often big numbers are used to represent cryptographic keys,
- * however, COSE which defines representations for keys chose not to
- * use this particular type.
+ * See also QCBOREncode_AddTPositiveBignumNoPreferred().
+ *
+ * In QCBOR v2, this is NOT backwards compatible with v1 by default. It
+ * can be made to be compatible with QCBOREncode_Setv1Compatibility().
+ * The incompatibility is that in v1 this function was a straight through
+ * copy, where for v2 this will use preferred serialization to encode
+ * small integers as type 1.
+ *
+ * Sometimes big numbers are used to represent parts of
+ * cryptographic keys, however, COSE which defines representations for
+ * keys does, not use this particular type.
  */
-static void
+void
 QCBOREncode_AddTPositiveBignum(QCBOREncodeContext *pCtx,
                                uint8_t             uTagRequirement,
-                               UsefulBufC          Bytes);
+                               UsefulBufC          BigNumber);
 
 static void
 QCBOREncode_AddTPositiveBignumToMapSZ(QCBOREncodeContext *pCtx,
                                       const char         *szLabel,
                                       uint8_t             uTagRequirement,
-                                      UsefulBufC          Bytes);
+                                      UsefulBufC          BigNumber);
 
 static void
 QCBOREncode_AddTPositiveBignumToMapN(QCBOREncodeContext *pCtx,
                                      int64_t             nLabel,
                                      uint8_t             uTagRequirement,
-                                     UsefulBufC          Bytes);
+                                     UsefulBufC          BigNumber);
 
 
+/**
+ * @brief Add a positive big number using preferred serialization.
+ *
+ * @param[in] pCtx             The encoding context to add the big number to.
+ * @param[in] BigNumber            Pointer and length of the big number.
+ *
+ * This operates the same as QCBOREncode_AddTPositiveBignum(),
+ * but always outputs the tag number. This exists for backwards
+ * compatibility with an early version of QCBOR.
+ *
+ * In QCBOR v2, this is NOT backwards compatible with v1 by default. It
+ * can be made to be compatible with QCBOREncode_Setv1Compatibility().
+ */
 static void
 QCBOREncode_AddPositiveBignum(QCBOREncodeContext *pCtx,
-                             UsefulBufC          Bytes);
+                              UsefulBufC          BigNumber);
 
 static void
 QCBOREncode_AddPositiveBignumToMap(QCBOREncodeContext *pCtx,
                                    const char         *szLabel,
-                                   UsefulBufC          Bytes);
+                                   UsefulBufC          BigNumber);
 
 static void
 QCBOREncode_AddPositiveBignumToMapN(QCBOREncodeContext *pCtx,
                                     int64_t             nLabel,
-                                    UsefulBufC          Bytes);
+                                    UsefulBufC          BigNumber);
 
 
 /**
- * @brief Add a negative big number to the encoded output.
+ * @brief Add a positive big number without preferred serialization.
  *
  * @param[in] pCtx             The encoding context to add the big number to.
  * @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or
  *                             @ref QCBOR_ENCODE_AS_BORROWED.
- * @param[in] Bytes            Pointer and length of the big number.
+ * @param[in] BigNumber            Pointer and length of the big number.
  *
- * Big numbers are integers larger than 64-bits. Their format is
- * described in [RFC 8949] (https://tools.ietf.org/html/rfc8949).
+ * This is mostly a straight-through output of the bytes that make up the big
+ * number. There is no conversion to CBOR type 0 integers for values
+ * smaller than @c UINT64_MAX.
  *
- * It is output as CBOR major type 2, a binary string, with tag
- * @ref CBOR_TAG_NEG_BIGNUM indicating the binary string is a negative
- * big number.
- *
- * Often big numbers are used to represent cryptographic keys,
- * however, COSE which defines representations for keys chose not to
- * use this particular type.
+ * See also QCBOREncode_AddTPositiveBignum().
  */
-static void
+void
+QCBOREncode_AddTPositiveBignumNoPreferred(QCBOREncodeContext *pCtx,
+                                          const uint8_t       uTagRequirement,
+                                          const UsefulBufC    BigNumber);
+
+void
+QCBOREncode_AddTPositiveBignumNoPreferredToMap(QCBOREncodeContext *pCtx,
+                                               const char         *szLabel,
+                                               const uint8_t       uTagRequirement,
+                                               const UsefulBufC    BigNumber);
+
+void
+QCBOREncode_AddTPositiveBignumNoPreferredToMapN(QCBOREncodeContext *pCtx,
+                                                int64_t             nLabel,
+                                                const uint8_t       uTagRequirement,
+                                                const UsefulBufC    BigNumber);
+
+/**
+ * @brief Add a negative big number using preferred serialization.
+ *
+ * @param[in] pCtx             The encoding context to add the big number to.
+ * @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or
+ *                             @ref QCBOR_ENCODE_AS_BORROWED.
+ * @param[in] BigNum            Pointer and length of the big number.
+ *
+ * This outputs @c BigNum as a type 1 if the value is greater than or
+ * equal to -(2^64), and as tag 3 if less than. This is as required
+ * for preferred serialization described in RFC 8949 3.4.3.
+ *
+ * Note that the actual encoded value is 1 more than the value of
+ * BigNum as required for all encoded negative numbers in CBOR. The
+ * implementation of this function does some big number arithmatic to
+ * effect this.
+ *
+ * See also QCBOREncode_AddTNegativeBignumNoPreferred().
+ *
+ * B y default, this function is not comparible with QCBOR v1. In
+ * QCBOR v1 this just copied @c BigNum straight through without
+ * adjusting the negative number by 1 and without preferred
+ * serialization. By calling QCBOREncode_Setv1Compatibility() , this
+ * will go back to the v1 behavior. This behavior in v2 is more
+ * correct.
+ */
+void
 QCBOREncode_AddTNegativeBignum(QCBOREncodeContext *pCtx,
                                uint8_t             uTagRequirement,
-                               UsefulBufC          Bytes);
+                               UsefulBufC          BigNum);
 
 static void
 QCBOREncode_AddTNegativeBignumToMapSZ(QCBOREncodeContext *pCtx,
@@ -1273,19 +1363,68 @@
                                      UsefulBufC          Bytes);
 
 
+/**
+ * @brief Add a negative big number using preferred serialization.
+ *
+ * @param[in] pCtx             The encoding context to add the big number to.
+ * @param[in] BigNumber            Pointer and length of the big number.
+ *
+ * This operates the same as QCBOREncode_AddTNegativeBignum(),
+ * but always outputs the tag number. This exists for backwards
+ * compatibility with an early version of QCBOR.
+ *
+ * In QCBOR v2, this is NOT backwards compatible with v1 by default. It
+ * can be made to be compatible with QCBOREncode_Setv1Compatibility().
+ */
 static void
 QCBOREncode_AddNegativeBignum(QCBOREncodeContext *pCtx,
-                              UsefulBufC          Bytes);
+                              UsefulBufC          BigNumber);
 
 static void
 QCBOREncode_AddNegativeBignumToMap(QCBOREncodeContext *pCtx,
                                    const char         *szLabel,
-                                   UsefulBufC          Bytes);
+                                   UsefulBufC          BigNumber);
 
 static void
 QCBOREncode_AddNegativeBignumToMapN(QCBOREncodeContext *pCtx,
                                     int64_t             nLabel,
-                                    UsefulBufC          Bytes);
+                                    UsefulBufC          BigNumber);
+
+
+/**
+ * @brief Add a negative big number without preferred serialization.
+ *
+ * @param[in] pCtx             The encoding context to add the big number to.
+ * @param[in] uTagRequirement  Either @ref QCBOR_ENCODE_AS_TAG or
+ *                             @ref QCBOR_ENCODE_AS_BORROWED.
+ * @param[in] BigNumber            Pointer and length of the big number.
+ *
+ * This always outputs the value as tag 3 (negative big number). It doesn't
+ * output numbers greater than -UINT64_MAX as type 1 integers as would
+ * be done with preferred serialization.
+ *
+ * This is not a straight through copy of the big number. It adds one, the
+ * offset used for all negative numbers in CBOR.
+ *
+ * If QCBOREncode_Setv1Compatibility() is set, then this does a
+ * straight-through copy without adding one.
+ *
+ * See also QCBOREncode_AddTNegativeBignum().
+ */
+void
+QCBOREncode_AddTNegativeBignumNoPreferred(QCBOREncodeContext *pCtx,
+                                          uint8_t             uTagRequirement,
+                                          UsefulBufC          BigNumber);
+void
+QCBOREncode_AddTNegativeBignumNoPreferredToMap(QCBOREncodeContext *pCtx,
+                                               const char         *szLabel,
+                                               uint8_t             uTagRequirement,
+                                               UsefulBufC          BigNumber);
+void
+QCBOREncode_AddTNegativeBignumNoPreferredToMapN(QCBOREncodeContext *pCtx,
+                                                int64_t             nLabel,
+                                                uint8_t             uTagRequirement,
+                                                UsefulBufC          BigNumber);
 
 
 #ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
@@ -2602,10 +2741,10 @@
 void
 QCBOREncode_Private_AddExpMantissa(QCBOREncodeContext *pCtx,
                                    uint64_t            uTag,
+                                   int64_t             nExponent,
                                    UsefulBufC          BigNumMantissa,
                                    bool                bBigNumIsNegative,
-                                   int64_t             nMantissa,
-                                   int64_t             nExponent);
+                                   int64_t             nMantissa);
 
 
 
@@ -2666,6 +2805,12 @@
 }
 
 static inline void
+QCBOREncode_SerializationAny(QCBOREncodeContext *pMe)
+{
+   pMe->uMode = QCBOR_ENCODE_MODE_ANY;
+}
+
+static inline void
 QCBOREncode_Allow(QCBOREncodeContext *pMe, const uint8_t uAllow)
 {
 #ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
@@ -2676,6 +2821,13 @@
 #endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
 }
 
+static inline void
+QCBOREncode_Setv1Compatibility(QCBOREncodeContext *pMe)
+{
+   pMe->uConfig = QCBOR_ENCODE_CONFIG_V1_COMPAT;
+}
+
+
 
 static inline void
 QCBOREncode_AddInt64ToMap(QCBOREncodeContext *pMe,
@@ -2723,12 +2875,15 @@
 
 
 static inline void
+QCBOREncode_AddNegativeUInt64(QCBOREncodeContext *pMe, const uint64_t uValue)
+{
+   QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_NEGATIVE_INT, uValue, 0);
+}
+
+static inline void
 QCBOREncode_AddNegativeUInt64ToMap(QCBOREncodeContext *pMe, const char *szLabel, uint64_t uNum)
 {
-   /* Use _AddBuffer() because _AddSZString() is defined below, not above */
-   QCBOREncode_Private_AddBuffer(pMe,
-                                 CBOR_MAJOR_TYPE_TEXT_STRING,
-                                 UsefulBuf_FromSZ(szLabel));
+   QCBOREncode_AddSZString(pMe, szLabel);
    QCBOREncode_AddNegativeUInt64(pMe, uNum);
 }
 
@@ -3109,16 +3264,6 @@
 }
 
 
-static inline void
-QCBOREncode_AddTPositiveBignum(QCBOREncodeContext *pMe,
-                               const uint8_t       uTagRequirement,
-                               const UsefulBufC    Bytes)
-{
-   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
-      QCBOREncode_AddTag(pMe, CBOR_TAG_POS_BIGNUM);
-   }
-   QCBOREncode_AddBytes(pMe, Bytes);
-}
 
 static inline void
 QCBOREncode_AddTPositiveBignumToMapSZ(QCBOREncodeContext *pMe,
@@ -3169,16 +3314,7 @@
 }
 
 
-static inline void
-QCBOREncode_AddTNegativeBignum(QCBOREncodeContext *pMe,
-                               const uint8_t       uTagRequirement,
-                               const UsefulBufC    Bytes)
-{
-   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
-      QCBOREncode_AddTag(pMe, CBOR_TAG_NEG_BIGNUM);
-   }
-   QCBOREncode_AddBytes(pMe, Bytes);
-}
+
 
 static inline void
 QCBOREncode_AddTNegativeBignumToMapSZ(QCBOREncodeContext *pMe,
@@ -3246,10 +3382,10 @@
    }
    QCBOREncode_Private_AddExpMantissa(pMe,
                                       uTag,
+                                      nBase10Exponent,
                                       NULLUsefulBufC,
                                       false,
-                                      nMantissa,
-                                      nBase10Exponent);
+                                      nMantissa);
 }
 
 static inline void
@@ -3334,10 +3470,10 @@
    }
    QCBOREncode_Private_AddExpMantissa(pMe,
                                       uTag,
+                                      nBase10Exponent,
                                       Mantissa,
                                       bIsNegative,
-                                      0,
-                                      nBase10Exponent);
+                                      0);
 }
 
 static inline void
@@ -3433,10 +3569,10 @@
    }
    QCBOREncode_Private_AddExpMantissa(pMe,
                                       uTag,
+                                      nBase2Exponent,
                                       NULLUsefulBufC,
                                       false,
-                                      nMantissa,
-                                      nBase2Exponent);
+                                      nMantissa);
 }
 
 static inline void
@@ -3515,10 +3651,10 @@
    }
    QCBOREncode_Private_AddExpMantissa(pMe,
                                       uTag,
+                                      nBase2Exponent,
                                       Mantissa,
                                       bIsNegative,
-                                      0,
-                                      nBase2Exponent);
+                                      0);
 }
 
 static inline void
@@ -3966,8 +4102,7 @@
 {
 #ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
    if(pMe->uMode >= QCBOR_ENCODE_MODE_DCBOR) {
-      if(uNum < CBOR_SIMPLEV_FALSE ||
-         uNum > CBOR_SIMPLEV_NULL) {
+      if(uNum < CBOR_SIMPLEV_FALSE || uNum > CBOR_SIMPLEV_NULL) {
          pMe->uError = QCBOR_ERR_NOT_PREFERRED;
          return;
       }
diff --git a/inc/qcbor/qcbor_private.h b/inc/qcbor/qcbor_private.h
index 0a3fb9c..d7abe31 100644
--- a/inc/qcbor/qcbor_private.h
+++ b/inc/qcbor/qcbor_private.h
@@ -231,12 +231,19 @@
 #define QCBOR_ENCODE_MODE_DCBOR     3
 
 
+/* Operate in compatibility with QCBOR 1.0
+ * So far the differences are:
+ *  - AddNegativeBigNum and AddBigFloat with a negative big num
+ */
+#define QCBOR_ENCODE_CONFIG_V1_COMPAT 0x01
+
 struct _QCBOREncodeContext {
    /* PRIVATE DATA STRUCTURE */
    UsefulOutBuf      OutBuf;  /* Pointer to output buffer, its length and
                                * position in it. */
    uint8_t           uError;  /* Error state, always from QCBORError enum */
    uint8_t           uMode;   /* @ref QCBOR_ENCODE_MODE_PREFERRED or related */
+   uint8_t           uConfig; /* QCBOR_ENCODE_CONFIG_xxx */
    uint8_t           uAllow;  /* @ref QCBOR_ENCODE_ALLOW_NAN_PAYLOAD, ... */
    void            (*pfnCloseMap)(QCBORPrivateEncodeContext *); /* Use of function
                                * pointer explained in QCBOREncode_SerializationCDE() */
diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h
index dc4baf7..26325d8 100644
--- a/inc/qcbor/qcbor_spiffy_decode.h
+++ b/inc/qcbor/qcbor_spiffy_decode.h
@@ -299,41 +299,53 @@
 
 #if !defined(USEFULBUF_DISABLE_ALL_FLOAT) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
 /**
- * @brief Decode next as a number with precision-preserving conversions.
+ * @brief dCBOR Decode next as a number with precision-preserving conversions.
  *
  * @param[in] pCtx           The decode context.
- * @param[out] pNumber       The returned 64-bit signed integer.
+ * @param[out] pNumber       The returned number.
  *
- * This will get the next item as a number and return it as a C
- * data type such that no precision is lost.
+ * This gets the next item as a number and returns it as a C data type
+ * such that no precision is lost.
  *
- * The CBOR input can be integers (major type 0 or 1) or floats (major type 7).
- * If not these \ref QCBOR_ERR_UNEXPECTED_TYPE will be set.
+ * This is primarily works with integers and floats for both the
+ * to-be-decoded CBOR and the decoded types.
+ *
+ * The CBOR input can be integers (major type 0 or 1) or floats (major
+ * type 7).  If not these, \ref QCBOR_ERR_UNEXPECTED_TYPE will be set.
  *
  * The conversion is as follows.
  *
- * Whole numbers between \c INT64_MIN and \c INT64_MAX will
- * be returned as \ref QCBOR_TYPE_INT64. This includes
- * conversion of float-point values that are whole numbers.
- *
- * Whole numbers between \c INT64_MAX and \c UINT64_MAX will
- * be returned as \ref QCBOR_TYPE_UINT64, again including
+ * Whole numbers from \c INT64_MIN to \c INT64_MAX will be returned as
+ * int64_t indicated as \ref QCBOR_TYPE_INT64. This includes
  * conversion of floating-point values that are whole numbers.
  *
- * The whole numbers called "65-bit negative" in CBOR (-2^63 - 1 to -2^64) are a
- * special case. Some of them can be converted to a double without
- * loss of precision and some can't (uint64_t has 64 bits of precision; a double
- * has only 52). If they can't be converted to a double, they are returned
- * as \ref QCBOR_TYPE_65BIT_NEG_INT.
- * In many cases, it will be reasonable to error out if the
- * number type returned here is \ref QCBOR_TYPE_65BIT_NEG_INT
- * on the assumption that many protocols will never uses these.
- * See also QCBOREncode_AddNegativeUInt64() for more discussion.
+ * Whole numbers from \c INT64_MAX +1 to \c UINT64_MAX will be
+ * returned as uint64_t indicated as \ref QCBOR_TYPE_UINT64, again
+ * including conversion of floating-point values that are whole
+ * numbers.
  *
- * All others are returned as a double.
+ * Most other numbers are returned as a double as indicated by 
+ * \ref QCBOR_TYPE_DOUBLE floating point with one set of exceptions.
  *
- * This is useful for dCBOR which essentially combines floats
- * and integers into one number space.
+ * The exception is negative whole numbers in the range of -(2^63 + 1)
+ * to -(2^64) that have too much precision to be represented as a
+ * double. Doubles have only 52 bits of precision, so they can't
+ * precisely represent every whole integer in this range. CBOR can
+ * represent these values with 64-bits of precision and when this
+ * function encounters them they are returned as \ref
+ * QCBOR_TYPE_65BIT_NEG_INT.  See the description of this type for
+ * instructions to gets its value.  Also see
+ * QCBORDecode_BignumPreferred().
+ *
+ * To give an example, the value -18446744073709551616 can't be
+ * represented by an int64_t or uint64_t, but can be represented by a
+ * double so it is returned by this function as a double. The value
+ * -18446744073709551617 however can't be represented by a double
+ * because it has too much precision, so it is returned as \ref
+ * QCBOR_TYPE_65BIT_NEG_INT.
+ *
+ * This is useful for DCBOR which essentially combines floats and
+ * integers into one number space.
  *
  * Please see @ref Decode-Errors-Overview "Decode Errors Overview".
  *
@@ -346,6 +358,68 @@
 #endif /* ! USEFULBUF_DISABLE_ALL_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
 
 /**
+ * @brief Decode next item as a big number encoded using preferred serialization.
+ *
+ * @param[in] pCtx          The decode context.
+ * @param[in] uTagRequirement  One of @c QCBOR_TAG_REQUIREMENT_XXX.
+ * @param[in] BigNumBuf     The buffer to write the result into.
+ * @param[in,out] pbIsNegative  Set to true if the resulting big number is negative.
+ * @param[out] pBigNum      The output big number.
+ *
+ * See QCBORDecode_PreferedBigNum() in full detail.
+ *
+ * The type processing rules are as follows.
+ *
+ * This always succeeds on type 0 and 1 integers (QCBOR_TYPE_INT64,
+ * QCBOR_TYPE_UINT64 and QCBOR_TYPE_65_BIT_NEG) no matter what
+ * uTagRequirement is. The rest of the rules pertain to what happens
+ * if the CBOR is not type 0 or type 1.
+ *
+ * If uTagRequirement is REQUIRE, this will fail on anything but a
+ * full and correct tag 2 or tag 3 big number.
+ *
+ * If uTagRequreiement is QCBOR_TAG_REQUIREMENT_NOT_A_TAG then this
+ * will fail on anything but a byte string.
+ *
+ * If QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG, then this will succeed on
+ * either a byte string or a tag 2 or 3.
+ *
+ * If the item is a bare byte string, not a tag 2 or 3, then
+ * pbIsNegative is an input parameter that determines the sign of the
+ * big number. The sign must be known because the decoding of a
+ * positive big number is different than a negative.
+ *
+ * TODO: reevaluate whether to call this GetBigNum after tag redesign
+ * It would be more consistent to be GetBigNum with its friend named GetBigNumNoPreferred
+ * but the parameters are different so backwards compat can be with a mode bit.
+ */
+void
+QCBORDecode_GetBigNumPreferred(QCBORDecodeContext *pCtx,
+                               uint8_t             uTagRequirement,
+                               UsefulBuf           BigNumBuf,
+                               UsefulBufC         *pBigNum,
+                               bool               *pbIsNegative);
+
+void
+QCBORDecode_GetBigNumPreferredInMapN(QCBORDecodeContext *pCtx,
+                                     uint8_t             uTagRequirement,
+                                     int64_t             nLabel,
+                                     UsefulBuf           BigNumBuf,
+                                     UsefulBufC         *pBigNum,
+                                     bool               *pbIsNegative);
+
+void
+QCBORDecode_GetBigNumPreferredInMapSZ(QCBORDecodeContext *pCtx,
+                                      uint8_t             uTagRequirement,
+                                      const char         *szLabel,
+                                      UsefulBuf           BigNumBuf,
+                                      UsefulBufC         *pBigNum,
+                                      bool               *pbIsNegative);
+
+
+
+
+/**
  * @brief Decode next item into a signed 64-bit integer with conversions.
  *
  * @param[in] pCtx           The decode context.
@@ -1465,9 +1539,10 @@
  *
  * The negative value is computed as -1 - n, where n is the postive
  * big number in @c pValue. There is no standard representation for
- * big numbers, positive or negative in C, so this implementation
+ * big numbers, positive or negative in C, so this function
  * leaves it up to the caller to apply this computation for negative
- * big numbers.
+ * big numbers, but QCBORDecode_BignumPreferred() can be
+ * used too.
  *
  * See @ref Tag-Usage for discussion on tag requirements.
  *
@@ -1479,14 +1554,13 @@
  * then the protocol design must have some way of indicating the sign.
  *
  * See also QCBORDecode_GetInt64ConvertAll(),
- * QCBORDecode_GetUInt64ConvertAll() and
- * QCBORDecode_GetDoubleConvertAll() which can convert big numbers.
+ * QCBORDecode_GetUInt64ConvertAll(),
+ * QCBORDecode_GetDoubleConvertAll() and QCBORDecode_BignumPreferred() which can convert big numbers.
  *
  * See also @ref CBOR_TAG_POS_BIGNUM, @ref CBOR_TAG_NEG_BIGNUM,
  * QCBOREncode_AddPositiveBignum(), QCBOREncode_AddNegativeBignum(),
  * @ref QCBOR_TYPE_POSBIGNUM and @ref QCBOR_TYPE_NEGBIGNUM.
  */
-// Improvement: Add function that converts integers and other to big nums
 void
 QCBORDecode_GetBignum(QCBORDecodeContext *pCtx,
                       uint8_t             uTagRequirement,
diff --git a/src/ieee754.c b/src/ieee754.c
index f9b7a3f..40421ad 100644
--- a/src/ieee754.c
+++ b/src/ieee754.c
@@ -691,6 +691,7 @@
    /* Cast safe because of mask above; exponents < DOUBLE_EXPONENT_MAX */
    const int64_t  nDoubleUnbiasedExponent = (int64_t)uDoubleBiasedExponent - DOUBLE_EXPONENT_BIAS;
    const uint64_t uDoubleSignificand      = uDouble & DOUBLE_SIGNIFICAND_MASK;
+   const uint64_t bIsNegative             = uDouble & DOUBLE_SIGN_MASK;
 
    if(nDoubleUnbiasedExponent == DOUBLE_EXPONENT_ZERO) {
       if(uDoubleSignificand == 0) {
@@ -706,13 +707,21 @@
          /* --- NAN --- */
          Result.type = IEEE754_ToInt_NaN; /* dCBOR doesn't care about payload */
       } else  {
-         /* --- INIFINITY --- */
+         /* --- INFINITY --- */
          Result.type = IEEE754_ToInt_NO_CONVERSION;
       }
-   } else if(nDoubleUnbiasedExponent < 0 ||
-             (nDoubleUnbiasedExponent >= ((uDouble & DOUBLE_SIGN_MASK) ? 63 : 64))) {
+   } else if(nDoubleUnbiasedExponent < 0) {
       /* --- Exponent out of range --- */
       Result.type = IEEE754_ToInt_NO_CONVERSION;
+   } else if(nDoubleUnbiasedExponent >= 64) {
+      if(nDoubleUnbiasedExponent == 64 && uDoubleSignificand == 0 && bIsNegative) {
+         /* Very special case for -18446744073709551616.0 */
+         Result.integer.un_signed = 0; /* No negative 0, use it to indicate 2^64 */
+         Result.type              = IEEE754_ToInt_IS_65BIT_NEG;
+      } else {
+         /* --- Exponent out of range --- */
+         Result.type = IEEE754_ToInt_NO_CONVERSION;
+      }
    } else {
       /* Conversion only fails when the input is too large or is not a
        * whole number, never because of lack of precision because
@@ -737,10 +746,15 @@
             /* Numbers greater than 2^52 with at most 52 significant bits */
             uInteger <<= nDoubleUnbiasedExponent - DOUBLE_NUM_SIGNIFICAND_BITS;
          }
-         if(uDouble & DOUBLE_SIGN_MASK) {
+         if(bIsNegative) {
             /* Cast safe because exponent range check above */
-            Result.integer.is_signed = -((int64_t)uInteger);
-            Result.type              = IEEE754_ToInt_IS_INT;
+            if(nDoubleUnbiasedExponent == 63) {
+               Result.integer.un_signed = uInteger;
+               Result.type              = IEEE754_ToInt_IS_65BIT_NEG;
+            } else {
+               Result.integer.is_signed = -((int64_t)uInteger);
+               Result.type              = IEEE754_ToInt_IS_INT;
+            }
          } else {
             Result.integer.un_signed = uInteger;
             Result.type              = IEEE754_ToInt_IS_UINT;
@@ -769,6 +783,8 @@
    /* Cast safe because of mask above; exponents < SINGLE_EXPONENT_MAX */
    const int32_t  nSingleUnbiasedExponent = (int32_t)uSingleBiasedExponent - SINGLE_EXPONENT_BIAS;
    const uint32_t uSingleleSignificand    = uSingle & SINGLE_SIGNIFICAND_MASK;
+   const uint64_t bIsNegative             = uSingle & SINGLE_SIGN_MASK;
+
 
    if(nSingleUnbiasedExponent == SINGLE_EXPONENT_ZERO) {
       if(uSingleleSignificand == 0 && !(uSingle & SINGLE_SIGN_MASK)) {
@@ -786,10 +802,18 @@
       } else  {
          Result.type = IEEE754_ToInt_NO_CONVERSION;
       }
-   } else if(nSingleUnbiasedExponent < 0 ||
-             (nSingleUnbiasedExponent >= ((uSingle & SINGLE_SIGN_MASK) ? 63 : 64))) {
+   } else if(nSingleUnbiasedExponent < 0) {
       /* --- Exponent out of range --- */
        Result.type = IEEE754_ToInt_NO_CONVERSION;
+   } else if(nSingleUnbiasedExponent >= 64) {
+      if(nSingleUnbiasedExponent == 64 && uSingleleSignificand == 0 && bIsNegative) {
+         /* Very special case for -18446744073709551616.0 */
+         Result.integer.un_signed = 0; /* No negative 0, use it to indicate 2^64 */
+         Result.type              = IEEE754_ToInt_IS_65BIT_NEG;
+      } else {
+         /* --- Exponent out of range --- */
+         Result.type = IEEE754_ToInt_NO_CONVERSION;
+      }
     } else {
       /* Conversion only fails when the input is too large or is not a
        * whole number, never because of lack of precision because
@@ -814,9 +838,15 @@
             /* Numbers greater than 2^23 with at most 23 significant bits*/
             uInteger <<= nSingleUnbiasedExponent - SINGLE_NUM_SIGNIFICAND_BITS;
          }
-         if(uSingle & SINGLE_SIGN_MASK) {
-            Result.integer.is_signed = -((int64_t)uInteger);
-            Result.type              = IEEE754_ToInt_IS_INT;
+         if(bIsNegative) {
+         /* Cast safe because exponent range check above */
+            if(nSingleUnbiasedExponent == 63) {
+               Result.integer.un_signed = uInteger;
+               Result.type              = IEEE754_ToInt_IS_65BIT_NEG;
+            } else {
+               Result.integer.is_signed = -((int64_t)uInteger);
+               Result.type              = IEEE754_ToInt_IS_INT;
+            }
          } else {
             Result.integer.un_signed = uInteger;
             Result.type              = IEEE754_ToInt_IS_UINT;
@@ -837,31 +867,37 @@
    uint64_t uDoubleSignificand;
    int      nPrecisionBits;
 
-   /* Figure out the exponent and normalize the significand. This is
-    * done by shifting out all leading zero bits and counting them. If
-    * none are shifted out, the exponent is 63. */
-   uDoubleSignificand = uInt;
-   nDoubleUnbiasedExponent = 63;
-   while(1) {
-      if(uDoubleSignificand & 0x8000000000000000UL) {
-         break;
+   if(uInt == 0) {
+      uDoubleSignificand      = 0;
+      nDoubleUnbiasedExponent = DOUBLE_EXPONENT_ZERO;
+
+   } else  {
+      /* Figure out the exponent and normalize the significand. This is
+       * done by shifting out all leading zero bits and counting them. If
+       * none are shifted out, the exponent is 63. */
+      uDoubleSignificand = uInt;
+      nDoubleUnbiasedExponent = 63;
+      while(1) {
+         if(uDoubleSignificand & 0x8000000000000000UL) {
+            break;
+         }
+         uDoubleSignificand <<= 1;
+         nDoubleUnbiasedExponent--;
+      };
+
+      /* Position significand correctly for a double. Only shift 63 bits
+       * because of the 1 that is present by implication in IEEE 754.*/
+      uDoubleSignificand >>= 63 - DOUBLE_NUM_SIGNIFICAND_BITS;
+
+      /* Subtract 1 which is present by implication in IEEE 754 */
+      uDoubleSignificand -= 1ULL << (DOUBLE_NUM_SIGNIFICAND_BITS);
+
+      nPrecisionBits = IEEE754_Private_CountPrecisionBits(uInt) - (64 - nDoubleUnbiasedExponent);
+
+      if(nPrecisionBits > DOUBLE_NUM_SIGNIFICAND_BITS) {
+         /* Will lose precision if converted */
+         return IEEE754_UINT_TO_DOUBLE_OOB;
       }
-      uDoubleSignificand <<= 1;
-      nDoubleUnbiasedExponent--;
-   };
-
-   /* Position significand correctly for a double. Only shift 63 bits
-    * because of the 1 that is present by implication in IEEE 754.*/
-   uDoubleSignificand >>= 63 - DOUBLE_NUM_SIGNIFICAND_BITS;
-
-   /* Subtract 1 which is present by implication in IEEE 754 */
-   uDoubleSignificand -= 1ULL << (DOUBLE_NUM_SIGNIFICAND_BITS);
-
-   nPrecisionBits = IEEE754_Private_CountPrecisionBits(uInt) - (64 - nDoubleUnbiasedExponent);
-
-   if(nPrecisionBits > DOUBLE_NUM_SIGNIFICAND_BITS) {
-      /* Will lose precision if converted */
-      return IEEE754_UINT_TO_DOUBLE_OOB;
    }
 
    return IEEE754_AssembleDouble((uint64_t)uIsNegative,
diff --git a/src/ieee754.h b/src/ieee754.h
index c82b9b4..fed28f1 100644
--- a/src/ieee754.h
+++ b/src/ieee754.h
@@ -97,6 +97,7 @@
 struct IEEE754_ToInt {
    enum {IEEE754_ToInt_IS_INT,
          IEEE754_ToInt_IS_UINT,
+         IEEE754_ToInt_IS_65BIT_NEG,
          IEEE754_ToInt_NO_CONVERSION,
          IEEE754_ToInt_NaN
    } type;
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 05363f0..41e6b21 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -895,12 +895,18 @@
  *
  * CBOR doesn't explicitly specify two's compliment for integers but
  * all CPUs use it these days and the test vectors in the RFC are
- * so. All integers in the CBOR structure are positive and the major
+ * so. All integers in encoded CBOR are unsigned and the CBOR major
  * type indicates positive or negative.  CBOR can express positive
- * integers up to 2^x - 1 where x is the number of bits and negative
- * integers down to 2^x.  Note that negative numbers can be one more
- * away from zero than positive.  Stdint, as far as I can tell, uses
- * two's compliment to represent negative integers.
+ * integers up to 2^64 - 1 negative integers down to -2^64.  Note that
+ * negative numbers can be one more
+ * away from zero than positive because there is no negative zero.
+ *
+ * The "65-bit negs" are values CBOR can encode that can't fit
+ * into an int64_t or uint64_t. They decoded as a special type
+ * QCBOR_TYPE_65BIT_NEG_INT. Not that this type does NOT
+ * take into account the offset of one for CBOR negative integers.
+ * It must be applied to get the correct value. Applying this offset
+ * would overflow a uint64_t.
  */
 static QCBORError
 QCBOR_Private_DecodeInteger(const int      nMajorType,
@@ -927,11 +933,8 @@
 
    } else {
       if(uArgument <= INT64_MAX) {
-         /* CBOR's representation of negative numbers lines up with
-          * the two-compliment representation. A negative integer has
-          * one more in range than a positive integer. INT64_MIN is
-          * equal to (-INT64_MAX) - 1.
-          */
+         /* INT64_MIN is one further away from 0 than INT64_MAX
+          * so the -1 here doesn't overflow. */
          pDecodedItem->val.int64 = (-(int64_t)uArgument) - 1;
          pDecodedItem->uDataType = QCBOR_TYPE_INT64;
 
@@ -1169,6 +1172,8 @@
 }
 
 
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
+
 #if !defined(QCBOR_DISABLE_DECODE_CONFORMANCE) && !defined(QCBOR_DISABLE_PREFERRED_FLOAT)
 
 static QCBORError
@@ -1256,8 +1261,7 @@
 
    return QCBOR_SUCCESS;
 }
-#else /* ! QCBOR_DISABLE_PREFERRED_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
-#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+#else /* ! QCBOR_DISABLE_DECODE_CONFORMANCE && ! QCBOR_DISABLE_PREFERRED_FLOAT */
 
 static QCBORError
 QCBORDecode_Private_SingleConformance(const float f, uint8_t uDecodeMode)
@@ -1280,11 +1284,9 @@
       return QCBOR_SUCCESS;
    }
 }
-#endif /* ! QCBOR_DISABLE_FLOAT_HW_USE */
 #endif /* ! QCBOR_DISABLE_DECODE_CONFORMANCE && ! QCBOR_DISABLE_PREFERRED_FLOAT */
 
 
-
 /*
  * Decode a float
  */
@@ -1318,7 +1320,6 @@
          break;
 
       case SINGLE_PREC_FLOAT: /* 26 */
-#ifndef USEFULBUF_DISABLE_ALL_FLOAT
          /* Single precision is normally returned as a double since
           * double is widely supported, there is no loss of precision,
           * it makes it easy for the caller in most cases and it can
@@ -1349,12 +1350,10 @@
           * and wants to save object code.
           */
 #endif /* ! QCBOR_DISABLE_FLOAT_HW_USE */
-#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
          uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS);
          break;
 
       case DOUBLE_PREC_FLOAT: /* 27 */
-#ifndef USEFULBUF_DISABLE_ALL_FLOAT
          pDecodedItem->val.dfnum = UsefulBufUtil_CopyUint64ToDouble(uArgument);
          pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
 
@@ -1362,13 +1361,14 @@
          if(uReturn != QCBOR_SUCCESS) {
             break;
          }
-#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
          uReturn = FLOAT_ERR_CODE_NO_FLOAT(QCBOR_SUCCESS);
          break;
    }
 
    return uReturn;
 }
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
+
 
 
 /* Make sure #define value line up as DecodeSimple counts on this. */
@@ -1437,7 +1437,11 @@
       case HALF_PREC_FLOAT: /* 25 */
       case SINGLE_PREC_FLOAT: /* 26 */
       case DOUBLE_PREC_FLOAT: /* 27 */
+#ifndef USEFULBUF_DISABLE_ALL_FLOAT
          uReturn = QCBOR_Private_DecodeFloat(uDecodeMode, nAdditionalInfo, uArgument, pDecodedItem);
+#else
+         uReturn = QCBOR_ERR_ALL_FLOAT_DISABLED;
+#endif /* ! USEFULBUF_DISABLE_ALL_FLOAT */
          break;
 
       case CBOR_SIMPLEV_FALSE: /* 20 */
@@ -1528,6 +1532,8 @@
    uint64_t uArgument = 0;
    int      nAdditionalInfo = 0;
 
+   memset(pDecodedItem, 0, sizeof(QCBORItem));
+
    /* Decode the "head" that every CBOR item has into the major type,
     * argument and the additional info.
     */
@@ -1539,12 +1545,11 @@
                                       &nMajorType,
                                       &uArgument,
                                       &nAdditionalInfo);
+
    if(uReturn != QCBOR_SUCCESS) {
       return uReturn;
    }
 
-   memset(pDecodedItem, 0, sizeof(QCBORItem));
-
    /* All the functions below get inlined by the optimizer. This code
     * is easier to read with them all being similar functions, even if
     * some functions don't do much.
@@ -5297,7 +5302,7 @@
  * numbers.
  */
 static QCBORError
-QCBOR_Private_ProcessBigNum(const uint8_t   uTagRequirement,
+QCBOR_Private_ProcessBigNum(const uint8_t    uTagRequirement,
                             const QCBORItem *pItem,
                             UsefulBufC      *pValue,
                             bool            *pbIsNegative)
@@ -5336,6 +5341,7 @@
                       bool               *pbIsNegative)
 {
    QCBORItem  Item;
+
    QCBORDecode_VGetNext(pMe, &Item);
    if(pMe->uLastError) {
       return;
@@ -6708,6 +6714,7 @@
 
       case QCBOR_TYPE_65BIT_NEG_INT:
 #ifndef QCBOR_DISABLE_FLOAT_HW_USE
+         // TODO: don't use float HW. We have the function to do it.
          *pdValue = -(double)pItem->val.uint64 - 1;
          break;
 #else
@@ -7715,15 +7722,17 @@
 {
    QCBORItem            Item;
    struct IEEE754_ToInt ToInt;
-   double               d;
+   double               dNum;
    QCBORError           uError;
 
    if(pMe->uLastError != QCBOR_SUCCESS) {
       return;
    }
 
+   // TODO:VGetNext?
    uError = QCBORDecode_GetNext(pMe, &Item);
    if(uError != QCBOR_SUCCESS) {
+      *pNumber = Item;
       pMe->uLastError = (uint8_t)uError;
       return;
    }
@@ -7772,15 +7781,25 @@
          }
          break;
 
-
       case QCBOR_TYPE_65BIT_NEG_INT:
-         d = IEEE754_UintToDouble(Item.val.uint64, 1);
-         if(d == IEEE754_UINT_TO_DOUBLE_OOB) {
-            *pNumber = Item;
-         } else {
+         if(Item.val.uint64 == UINT64_MAX) {
+            /* The value -18446744073709551616 is encoded as an
+             * unsigned 18446744073709551615. It's a whole number that
+             * needs to be returned as a double. It can't be handled
+             * by IEEE754_UintToDouble because 18446744073709551616
+             * doesn't fit into a uint64_t. You can't get it by adding
+             * 1 to 18446744073709551615.
+             */
+            pNumber->val.dfnum = -18446744073709551616.0;
             pNumber->uDataType = QCBOR_TYPE_DOUBLE;
-            /* -1 is because of CBOR offset of negative numbers */
-            pNumber->val.dfnum = d - 1;
+         } else {
+            dNum = IEEE754_UintToDouble(Item.val.uint64 + 1, 1);
+            if(dNum == IEEE754_UINT_TO_DOUBLE_OOB) {
+               *pNumber = Item;
+            } else {
+               pNumber->val.dfnum = dNum;
+               pNumber->uDataType = QCBOR_TYPE_DOUBLE;
+            }
          }
          break;
 
@@ -7792,3 +7811,317 @@
 }
 
 #endif /* ! USEFULBUF_DISABLE_ALL_FLOAT && ! QCBOR_DISABLE_PREFERRED_FLOAT */
+
+
+static UsefulBufC
+QCBORDecode_IntToBigNum(uint64_t         uNum,
+                        const UsefulBuf  BigNumBuf)
+{
+   UsefulOutBuf OB;
+
+   /* With a UsefulOutBuf, there's no pointer math here. */
+   UsefulOutBuf_Init(&OB, BigNumBuf);
+
+   /* Must copy one byte even if zero.  The loop, mask and shift
+    * algorithm provides endian conversion.
+    */
+   do {
+      UsefulOutBuf_InsertByte(&OB, uNum & 0xff, 0);
+      uNum >>= 8;
+   } while(uNum);
+
+   return UsefulOutBuf_OutUBuf(&OB);
+}
+
+
+static UsefulBufC
+QCBORDecode_Private_RemoveLeadingZeros(UsefulBufC String)
+{
+   while(String.len > 1) {
+      if(*(const uint8_t *)String.ptr) {
+         break;
+      }
+      String.len--;
+      String.ptr = (const uint8_t *)String.ptr + 1;
+   }
+
+   return String;
+}
+
+
+/* Add one to the big number and put the result in a new UsefulBufC
+ * from storage in UsefulBuf.
+ *
+ * Leading zeros must be removed before calling this.
+ *
+ * Code Reviewers: THIS FUNCTION DOES POINTER MATH
+ */
+static UsefulBufC
+QCBORDecode_BigNumCopyPlusOne(UsefulBufC BigNum,
+                              UsefulBuf  BigNumBuf)
+{
+   uint8_t        uCarry;
+   uint8_t        uSourceValue;
+   const uint8_t *pSource;
+   uint8_t       *pDest;
+   ptrdiff_t      uDestBytesLeft;
+
+   /* Start adding at the LSB */
+   pSource = &((const uint8_t *)BigNum.ptr)[BigNum.len-1];
+   pDest   = &((uint8_t *)BigNumBuf.ptr)[BigNumBuf.len-1];
+
+   uCarry = 1; /* Gets set back to zero if add the next line doesn't wrap */
+   *pDest = *pSource + 1;
+   while(1) {
+      /* Wrap around from 0xff to 0 is a defined operation for
+	 unsigned addition in C. */
+      if(*pDest != 0) {
+         /*  The add operation didn't wrap so no more carry. This
+          * funciton only adds one, so when there is no more carry,
+          * carrying is over to the end.
+          */
+         uCarry = 0;
+      }
+
+      uDestBytesLeft = pDest - (uint8_t *)BigNumBuf.ptr;
+      if(pSource <= (const uint8_t *)BigNum.ptr && uCarry == 0) {
+         break; /* Successful exit */
+      }
+      if(pSource > (const uint8_t *)BigNum.ptr) {
+         uSourceValue = *--pSource;
+      } else {
+         /* All source bytes processed, but not the last carry */
+         uSourceValue = 0;
+      }
+
+      pDest--;
+      if(uDestBytesLeft < 0) {
+         return NULLUsefulBufC; /* Not enough space in destination buffer */
+      }
+
+      *pDest = uSourceValue + uCarry;
+   }
+
+   return (UsefulBufC){pDest, BigNumBuf.len - (size_t)uDestBytesLeft};
+}
+
+
+/* This returns 1 when uNum is 0 */
+static size_t
+QCBORDecode_Private_CountNonZeroBytes(uint64_t uNum)
+{
+   size_t uCount = 0;
+   do {
+      uCount++;
+      uNum >>= 8;
+   } while(uNum);
+
+   return uCount;
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h
+ */
+QCBORError
+QCBORDecode_BignumPreferred(const QCBORItem Item,
+                            UsefulBuf       BigNumBuf,
+                            UsefulBufC     *pBigNum,
+                            bool           *pbIsNegative)
+{
+   QCBORError  uResult;
+   size_t      uLen;
+   UsefulBufC  BigNum;
+   int         uType;
+
+   uType = Item.uDataType;
+   if(uType == QCBOR_TYPE_BYTE_STRING) {
+      uType = *pbIsNegative ? QCBOR_TYPE_POSBIGNUM : QCBOR_TYPE_NEGBIGNUM;
+   }
+
+   static const uint8_t Zero[] = {0x00};
+   BigNum = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(Zero);
+   if((uType == QCBOR_TYPE_POSBIGNUM || uType == QCBOR_TYPE_NEGBIGNUM) &&
+       Item.val.bigNum.len) {
+         BigNum = QCBORDecode_Private_RemoveLeadingZeros(Item.val.bigNum);
+   }
+
+   /* Compute required length so it can be returned if buffer is too small */
+   switch(uType) {
+      case QCBOR_TYPE_INT64:
+         uLen = QCBORDecode_Private_CountNonZeroBytes((uint64_t)(Item.val.int64 < 0 ? -Item.val.int64 : Item.val.int64));
+         break;
+
+      case QCBOR_TYPE_UINT64:
+         uLen = QCBORDecode_Private_CountNonZeroBytes(Item.val.uint64);
+         break;
+
+      case QCBOR_TYPE_65BIT_NEG_INT:
+         uLen = Item.val.uint64 == UINT64_MAX  ? 9 : QCBORDecode_Private_CountNonZeroBytes(Item.val.uint64);
+         break;
+
+      case QCBOR_TYPE_POSBIGNUM:
+         uLen = BigNum.len;
+         break;
+
+      case QCBOR_TYPE_NEGBIGNUM:
+         uLen = BigNum.len;
+         if(UsefulBuf_IsValue(BigNum, 0xff) == SIZE_MAX) {
+            uLen++;
+         }
+         break;
+
+      default:
+         uLen = 0;
+   }
+
+   *pBigNum = (UsefulBufC){NULL, uLen};
+
+   if(BigNumBuf.len < uLen || uLen == 0 || BigNumBuf.ptr == NULL) {
+      return BigNumBuf.ptr == NULL ? QCBOR_SUCCESS : QCBOR_ERR_BUFFER_TOO_SMALL;
+      /* Buffer is too short or type is wrong */
+   }
+
+   uResult = QCBOR_SUCCESS;
+
+   if(uType == QCBOR_TYPE_POSBIGNUM) {
+      *pBigNum = UsefulBuf_Copy(BigNumBuf, BigNum);
+      *pbIsNegative = false;
+   } else if(uType == QCBOR_TYPE_UINT64) {
+      *pBigNum = QCBORDecode_IntToBigNum(Item.val.uint64, BigNumBuf);
+      *pbIsNegative = false;
+   } else if(uType == QCBOR_TYPE_INT64) {
+      *pbIsNegative = Item.val.int64 < 0;
+      *pBigNum = QCBORDecode_IntToBigNum((uint64_t)(*pbIsNegative ? -Item.val.int64 : Item.val.int64), BigNumBuf);
+   } else if(uType == QCBOR_TYPE_65BIT_NEG_INT) {
+      *pbIsNegative = true;
+      if(Item.val.uint64 == UINT64_MAX) {
+         /* The one value that can't be done with a computation
+          * because it would overflow a uint64_t*/
+         static const uint8_t TwoToThe64[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+         *pBigNum = UsefulBuf_Copy(BigNumBuf, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(TwoToThe64));
+      } else {
+         *pBigNum = QCBORDecode_IntToBigNum(Item.val.uint64 + 1, BigNumBuf);
+      }
+   } else if(uType == QCBOR_TYPE_NEGBIGNUM) {
+      /* The messy one. Take the stuff in the buffer and copy it to
+       * the new buffer, adding one to it. This might be one byte
+       * bigger than the original because of the carry from adding
+       * one.*/
+      *pbIsNegative = true;
+      *pBigNum = QCBORDecode_BigNumCopyPlusOne(BigNum, BigNumBuf);
+
+   } else {
+      uResult = QCBOR_ERR_UNEXPECTED_TYPE;
+   }
+
+   return uResult;
+}
+
+
+static QCBORError
+QCBOR_Private_ProcessPreferredBigNum(const uint8_t    uTagRequirement,
+                                     const QCBORItem *pItem,
+                                     UsefulBuf        BigNumBuf,
+                                     UsefulBufC      *pValue,
+                                     bool            *pbIsNegative)
+{
+   if(pItem->uDataType != QCBOR_TYPE_INT64 &&
+      pItem->uDataType != QCBOR_TYPE_UINT64 &&
+      pItem->uDataType != QCBOR_TYPE_65BIT_NEG_INT) {
+
+      /* The integer types are always OK. If it's not an integer type drop
+       * in to the tag type checking system. */
+      const QCBOR_Private_TagSpec TagSpec =
+      {
+         uTagRequirement,
+         {QCBOR_TYPE_POSBIGNUM, QCBOR_TYPE_NEGBIGNUM, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE},
+         {QCBOR_TYPE_BYTE_STRING, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE, QCBOR_TYPE_NONE}
+      };
+
+      QCBORError uErr = QCBOR_Private_CheckTagRequirement(TagSpec, pItem);
+      if(uErr != QCBOR_SUCCESS) {
+         return uErr;
+      }
+   }
+
+   return QCBORDecode_BignumPreferred(*pItem, BigNumBuf, pValue, pbIsNegative);
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h
+ */
+void
+QCBORDecode_GetBigNumPreferred(QCBORDecodeContext *pMe,
+                               const uint8_t       uTagRequirement,
+                               UsefulBuf           BigNumBuf,
+                               UsefulBufC         *pValue,
+                               bool               *pbIsNegative)
+{
+   QCBORItem Item;
+
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      /* Already in error state, do nothing */
+      return;
+   }
+
+   QCBORError uError = QCBORDecode_GetNext(pMe, &Item);
+   if(uError != QCBOR_SUCCESS) {
+      pMe->uLastError = (uint8_t)uError;
+      return;
+   }
+
+   QCBOR_Private_ProcessPreferredBigNum(uTagRequirement, &Item, BigNumBuf, pValue, pbIsNegative);
+}
+
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h
+ */
+void
+QCBORDecode_GetPreferredBignumInMapN(QCBORDecodeContext *pMe,
+                                     const int64_t       nLabel,
+                                     const uint8_t       uTagRequirement,
+                                     UsefulBuf           BigNumBuf,
+                                     UsefulBufC         *pValue,
+                                     bool               *pbIsNegative)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapN(pMe, nLabel, QCBOR_TYPE_ANY, &Item);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)QCBOR_Private_ProcessPreferredBigNum(uTagRequirement,
+                                                                  &Item,
+                                                                   BigNumBuf,
+                                                                   pValue,
+                                                                   pbIsNegative);
+}
+
+/*
+ * Public function, see header qcbor/qcbor_decode.h
+ */
+void
+QCBORDecode_GetPreferredBignumInMapSZ(QCBORDecodeContext *pMe,
+                                      const char *       szLabel,
+                                      const uint8_t       uTagRequirement,
+                                      UsefulBuf           BigNumBuf,
+                                      UsefulBufC         *pValue,
+                                      bool               *pbIsNegative)
+{
+   QCBORItem Item;
+   QCBORDecode_GetItemInMapSZ(pMe, szLabel, QCBOR_TYPE_ANY, &Item);
+   if(pMe->uLastError != QCBOR_SUCCESS) {
+      return;
+   }
+
+   pMe->uLastError = (uint8_t)QCBOR_Private_ProcessPreferredBigNum(uTagRequirement,
+                                                                  &Item,
+                                                                   BigNumBuf,
+                                                                   pValue,
+                                                                   pbIsNegative);
+}
+
+
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index 7d2f7fb..fa7f485 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -580,29 +580,6 @@
 
 
 /*
- * Public functions for adding negative integers. See qcbor/qcbor_encode.h
- */
-void
-QCBOREncode_AddNegativeUInt64(QCBOREncodeContext *pMe, const uint64_t uValue)
-{
-#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
-   if(pMe->uMode >= QCBOR_ENCODE_MODE_DCBOR) {
-      /* Never allowed in dCBOR */
-      pMe->uError = QCBOR_ERR_NOT_PREFERRED;
-      return;
-   }
-
-   if(!(pMe->uAllow & QCBOR_ENCODE_ALLOW_65_BIG_NEG)) {
-      pMe->uError = QCBOR_ERR_NOT_ALLOWED;
-      return;
-   }
-#endif /* QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
-
-   QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_NEGATIVE_INT, uValue, 0);
-}
-
-
-/*
  * Public functions for adding signed integers. See qcbor/qcbor_encode.h
  */
 void
@@ -679,6 +656,7 @@
    IEEE754_union        FloatResult;
    bool                 bNoNaNPayload;
    struct IEEE754_ToInt IntResult;
+   uint64_t             uNegValue;
 
 #ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
    if(IEEE754_DoubleHasNaNPayload(dNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
@@ -696,6 +674,16 @@
          case IEEE754_ToInt_IS_UINT:
             QCBOREncode_AddUInt64(pMe, IntResult.integer.un_signed);
             return;
+         case IEEE754_ToInt_IS_65BIT_NEG:
+            {
+               if(IntResult.integer.un_signed == 0) {
+                  uNegValue = UINT64_MAX;
+               } else {
+                  uNegValue = IntResult.integer.un_signed-1;
+               }
+               QCBOREncode_AddNegativeUInt64(pMe, uNegValue);
+            }
+            return;
          case IEEE754_ToInt_NaN:
             dNum = NAN;
             bNoNaNPayload = true;
@@ -728,6 +716,7 @@
    IEEE754_union        FloatResult;
    bool                 bNoNaNPayload;
    struct IEEE754_ToInt IntResult;
+   uint64_t             uNegValue;
 
 #ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
    if(IEEE754_SingleHasNaNPayload(fNum) && !(pMe->uAllow & QCBOR_ENCODE_ALLOW_NAN_PAYLOAD)) {
@@ -745,6 +734,16 @@
          case IEEE754_ToInt_IS_UINT:
             QCBOREncode_AddUInt64(pMe, IntResult.integer.un_signed);
             return;
+         case IEEE754_ToInt_IS_65BIT_NEG:
+            {
+               if(IntResult.integer.un_signed == 0) {
+                  uNegValue = UINT64_MAX;
+               } else {
+                  uNegValue = IntResult.integer.un_signed-1;
+               }
+               QCBOREncode_AddNegativeUInt64(pMe, uNegValue);
+            }
+            return;
          case IEEE754_ToInt_NaN:
             fNum = NAN;
             bNoNaNPayload = true;
@@ -764,6 +763,302 @@
 
 
 
+
+/* Actual addition of a positive/negative big num tag */
+static void
+QCBOREncode_Private_AddTBignum(QCBOREncodeContext *pMe,
+                               const uint64_t      uTag,
+                               const uint8_t       uTagRequirement,
+                               const UsefulBufC    BigNum)
+{
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      QCBOREncode_AddTag(pMe, uTag);
+   }
+   QCBOREncode_AddBytes(pMe, BigNum);
+}
+
+
+/* Add a positive/negative big num, non-preferred */
+static void
+QCBOREncode_Private_AddTBignumNoPreferred(QCBOREncodeContext *pMe,
+                                          const uint64_t      uTag,
+                                          const uint8_t       uTagRequirement,
+                                          const UsefulBufC    BigNum)
+{
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+   if(pMe->uMode >= QCBOR_ENCODE_MODE_PREFERRED) {
+      pMe->uError = QCBOR_ERR_NOT_PREFERRED;
+      return;
+   }
+#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
+   QCBOREncode_Private_AddTBignum(pMe, uTag, uTagRequirement, BigNum);
+}
+
+
+/* Is there a carry when you add 1 to the BigNum? */
+static bool
+QCBOREncode_Private_BigNumCarry(UsefulBufC BigNum)
+{
+   bool       bCarry;
+   UsefulBufC SubBigNum;
+
+   if(BigNum.len == 0) {
+      return true; /* Adding one to zero-length string gives a carry */
+   } else {
+      SubBigNum = UsefulBuf_Tail(BigNum, 1);
+      bCarry = QCBOREncode_Private_BigNumCarry(SubBigNum);
+      if(*(const uint8_t *)BigNum.ptr == 0x00 && bCarry) {
+         return true;
+      } else {
+         return false;
+      }
+   }
+}
+
+
+/*
+ * Output negative bignum bytes with subtraction of 1
+ */
+void
+QCBOREncode_Private_AddTNegativeBignum(QCBOREncodeContext *pMe,
+                                       const uint8_t       uTagRequirement,
+                                       const UsefulBufC    BigNum)
+{
+   size_t     uLen;
+   bool       bCarry;
+   bool       bCopiedSomething;
+   uint8_t    uByte;
+   UsefulBufC SubString;
+   UsefulBufC NextSubString;
+
+   if(uTagRequirement == QCBOR_ENCODE_AS_TAG) {
+      QCBOREncode_AddTag(pMe, CBOR_TAG_NEG_BIGNUM);
+   }
+
+   /* This works on any length without the need of an additional buffer */
+
+   /* This subtracts one, possibly making the string shorter by one
+    * 0x01 -> 0x00
+    * 0x01 0x00 -> 0xff
+    * 0x00 0x01 0x00 -> 0x00 0xff
+    * 0x02 0x00 -> 0x01 0xff
+    * 0xff -> 0xfe
+    * 0xff 0x00 -> 0xfe 0xff
+    * 0x01 0x00 0x00 -> 0xff 0xff
+    */
+
+   /* Compute the length up front because it goes in the head */
+   bCarry = QCBOREncode_Private_BigNumCarry(UsefulBuf_Tail(BigNum, 1));
+   uLen = BigNum.len;
+   if(bCarry && *(const uint8_t *)BigNum.ptr >= 1 && BigNum.len > 1) {
+      uLen--;
+   }
+   QCBOREncode_Private_AppendCBORHead(pMe, CBOR_MAJOR_TYPE_BYTE_STRING, uLen, 0);
+
+   SubString = BigNum;
+   bCopiedSomething = false;
+   while(SubString.len) {
+      uByte = *((const uint8_t *)SubString.ptr);
+      NextSubString = UsefulBuf_Tail(SubString, 1);
+      bCarry = QCBOREncode_Private_BigNumCarry(NextSubString);
+      if(bCarry) {
+         uByte--;
+      }
+      if(bCopiedSomething || NextSubString.len == 0 || uByte != 0) { /* No leading zeros, but one zero is OK */
+         UsefulOutBuf_AppendByte(&(pMe->OutBuf), uByte);
+         bCopiedSomething = true;
+      }
+      SubString = NextSubString;
+   }
+}
+
+
+static UsefulBufC
+QCBOREncode_Private_RemoveLeadingZeros(UsefulBufC String)
+{
+   while(String.len > 1) {
+      if(*(const uint8_t *)String.ptr) {
+         break;
+      }
+      String.len--;
+      String.ptr = (const uint8_t *)String.ptr + 1;
+   }
+
+   return String;
+}
+
+
+/*
+ * Public function. See qcbor/qcbor_encode.h
+ */
+void
+QCBOREncode_AddTNegativeBignumNoPreferred(QCBOREncodeContext *pMe,
+                                          const uint8_t       uTagRequirement,
+                                          const UsefulBufC    BigNum)
+{
+#ifndef QCBOR_DISABLE_ENCODE_USAGE_GUARDS
+   if(pMe->uMode >= QCBOR_ENCODE_MODE_PREFERRED) {
+      pMe->uError = QCBOR_ERR_NOT_PREFERRED;
+      return;
+   }
+#endif /* ! QCBOR_DISABLE_ENCODE_USAGE_GUARDS */
+
+   if(UsefulBuf_IsValue(BigNum, 0) == SIZE_MAX) {
+      pMe->uError = QCBOR_ERR_NO_NEGATIVE_ZERO;
+      return;
+   }
+
+   if(pMe->uConfig & QCBOR_ENCODE_CONFIG_V1_COMPAT) {
+      QCBOREncode_Private_AddTBignum(pMe, CBOR_TAG_NEG_BIGNUM, uTagRequirement, BigNum);
+   } else {
+      QCBOREncode_Private_AddTNegativeBignum(pMe, uTagRequirement, QCBOREncode_Private_RemoveLeadingZeros(BigNum));
+   }
+}
+
+
+void
+QCBOREncode_AddTNegativeBignumNoPreferredToMap(QCBOREncodeContext *pMe,
+                                               const char         *szLabel,
+                                               uint8_t             uTagRequirement,
+                                               UsefulBufC          BigNumber)
+{
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_AddTNegativeBignumNoPreferred(pMe, uTagRequirement, BigNumber);
+}
+
+
+void
+QCBOREncode_AddTNegativeBignumNoPreferredToMapN(QCBOREncodeContext *pMe,
+                                                int64_t             nLabel,
+                                                uint8_t             uTagRequirement,
+                                                UsefulBufC          BigNumber)
+{
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_AddTNegativeBignumNoPreferred(pMe, uTagRequirement, BigNumber);
+}
+
+/*
+ * Public function. See qcbor/qcbor_encode.h
+ */
+void
+QCBOREncode_AddTPositiveBignumNoPreferred(QCBOREncodeContext *pMe,
+                                          const uint8_t       uTagRequirement,
+                                          const UsefulBufC    BigNum)
+{
+   QCBOREncode_Private_AddTBignumNoPreferred(pMe, CBOR_TAG_POS_BIGNUM, uTagRequirement, BigNum);
+}
+
+void
+QCBOREncode_AddTPositiveBignumNoPreferredToMap(QCBOREncodeContext *pMe,
+                                               const char         *szLabel,
+                                               const uint8_t       uTagRequirement,
+                                               const UsefulBufC    BigNum)
+{
+   QCBOREncode_AddSZString(pMe, szLabel);
+   QCBOREncode_Private_AddTBignumNoPreferred(pMe, CBOR_TAG_POS_BIGNUM, uTagRequirement, BigNum);
+}
+
+void
+QCBOREncode_AddTPositiveBignumNoPreferredToMapN(QCBOREncodeContext *pMe,
+                                                int64_t             nLabel,
+                                                const uint8_t       uTagRequirement,
+                                                const UsefulBufC    BigNum)
+{
+   QCBOREncode_AddInt64(pMe, nLabel);
+   QCBOREncode_Private_AddTBignumNoPreferred(pMe, CBOR_TAG_POS_BIGNUM, uTagRequirement, BigNum);
+}
+
+
+
+
+/* This will return an erroneous value if BigNum.len > 8 */
+/* Convert from bignum to uint with endianess conversion */
+static uint64_t
+QCBOREncode_Private_BigNumToUInt(const UsefulBufC BigNum)
+{
+   uint64_t uInt;
+   size_t   uIndex;
+
+   uInt = 0;
+   for(uIndex = 0; uIndex < BigNum.len; uIndex++) {
+      uInt = (uInt << 8) + ((const uint8_t *)BigNum.ptr)[uIndex];
+   }
+
+   return uInt;
+}
+
+
+/*
+ * Public function. See qcbor/qcbor_encode.h
+ */
+void
+QCBOREncode_AddTPositiveBignum(QCBOREncodeContext *pMe,
+                               const uint8_t       uTagRequirement,
+                               const UsefulBufC    BigNum)
+{
+   if(pMe->uConfig & QCBOR_ENCODE_CONFIG_V1_COMPAT) {
+      QCBOREncode_AddTPositiveBignumNoPreferred(pMe, uTagRequirement, BigNum);
+   } else {
+      const UsefulBufC BigNumNLZ = QCBOREncode_Private_RemoveLeadingZeros(BigNum);
+      if(BigNumNLZ.len <= 8) {
+         /* Preferred serialization requires conversion to type 0 */
+         QCBOREncode_AddUInt64(pMe, QCBOREncode_Private_BigNumToUInt(BigNumNLZ));
+      } else {
+         QCBOREncode_Private_AddTBignum(pMe, CBOR_TAG_POS_BIGNUM, uTagRequirement, BigNumNLZ);
+      }
+   }
+}
+
+
+/*
+ * Public function. See qcbor/qcbor_encode.h
+ */
+void
+QCBOREncode_AddTNegativeBignum(QCBOREncodeContext *pMe,
+                               const uint8_t       uTagRequirement,
+                               const UsefulBufC    BigNum)
+{
+   uint64_t   uInt;
+   bool       bIs2exp64;
+   static const uint8_t twoExp64[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+   if(UsefulBuf_IsValue(BigNum, 0) == SIZE_MAX) {
+      pMe->uError = QCBOR_ERR_NO_NEGATIVE_ZERO;
+      return;
+   }
+
+   if(pMe->uConfig & QCBOR_ENCODE_CONFIG_V1_COMPAT) {
+      QCBOREncode_AddTNegativeBignumNoPreferred(pMe, uTagRequirement, BigNum);
+
+   } else {
+      /* Here we do preferred serialization. That requires removal of leading zeros */
+      const UsefulBufC BigNumNLZ = QCBOREncode_Private_RemoveLeadingZeros(BigNum);
+
+      bIs2exp64 = ! UsefulBuf_Compare(BigNumNLZ, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(twoExp64));
+
+      if(BigNumNLZ.len <= 8 || bIs2exp64) {
+         /* Must convert to CBOR type 1, a negative integer */
+         if(bIs2exp64) {
+            /* 2^64 is a 9 byte big number. Since negative numbers are offset
+             * by one in CBOR, it can be encoded as a type 1 negative. The
+             * conversion below won't work because the uInt will overflow
+             * before the subtraction of 1.
+             */
+            uInt = UINT64_MAX;
+         } else {
+            uInt = QCBOREncode_Private_BigNumToUInt(BigNumNLZ);
+            uInt--; /* CBOR's negative offset of 1  */
+         }
+         QCBOREncode_AddNegativeUInt64(pMe, uInt);
+
+      } else {
+         QCBOREncode_Private_AddTNegativeBignum(pMe, uTagRequirement, BigNumNLZ);
+      }
+   }
+}
+
+
 #ifndef QCBOR_DISABLE_EXP_AND_MANTISSA
 /**
  * @brief  Semi-private method to add bigfloats and decimal fractions.
@@ -798,10 +1093,10 @@
 void
 QCBOREncode_Private_AddExpMantissa(QCBOREncodeContext *pMe,
                                    const uint64_t      uTag,
+                                   const int64_t       nExponent,
                                    const UsefulBufC    BigNumMantissa,
                                    const bool          bBigNumIsNegative,
-                                   const int64_t       nMantissa,
-                                   const int64_t       nExponent)
+                                   const int64_t       nMantissa)
 {
    /* This is for encoding either a big float or a decimal fraction,
     * both of which are an array of two items, an exponent and a
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),