simpler float decoding to parallel simpler float encoding. Interface uses only a double
diff --git a/README.md b/README.md
index 44195da..b4eebc4 100644
--- a/README.md
+++ b/README.md
@@ -25,12 +25,10 @@
 ## Code Status
 QCBOR was originally developed by Qualcomm. It was [open sourced through CAF](https://source.codeaurora.org/quic/QCBOR/QCBOR/) with a permissive Linux license, September 2018 (thanks Qualcomm!).
 
-This code in Laurence's GitHub has diverged some from the CAF source with some small simplifications and tidying up.  The full test suite is not up and running and available in GitHub yet, so some caution is advised. This should be remedies soon.
+This code in Laurence's GitHub has diverged some from the CAF source with some small simplifications and tidying up.  T
 
-The following modifications are planned:
-* Improve design for handling multiple tags
-
-These changes may result in some interface changes. 
+From Nov 3 on, the interface and code are fairly stable. Large changes are not planned or expected, particularly in the interface. The test coverage
+is pretty good.
 
 ## Building
 There is a simple makefile for the UNIX style command line binary that compiles everything to run the tests.
@@ -41,6 +39,7 @@
 * src/UsefulBuf.c
 * src/qcbor_encode.c
 * src/qcbor_decode.c
+* src/qcbor_decode_malloc.c (optional; only for indefinite length strings you want handled with malloc)
 * src/ieee754.h
 * src/ieee754.c
 
diff --git a/inc/qcbor.h b/inc/qcbor.h
index 7f72413..741dfbc 100644
--- a/inc/qcbor.h
+++ b/inc/qcbor.h
@@ -692,7 +692,6 @@
 
       UsefulBufC  string;     /** The value for uDataType QCBOR_TYPE_BYTE_STRING and QCBOR_TYPE_TEXT_STRING */
       uint16_t    uCount;     /** The "value" for uDataType QCBOR_TYPE_ARRAY or QCBOR_TYPE_MAP -- the number of items in the array or map */ // TODO: indefinite len arrays
-      float       fnum;       /** The value for uDataType QCBOR_TYPE_FLOAT */
       double      dfnum;      /** The value for uDataType QCBOR_TYPE_DOUBLE */
       struct {
          int64_t  nSeconds;
diff --git a/src/ieee754.c b/src/ieee754.c
index a74fa78..40d5739 100644
--- a/src/ieee754.c
+++ b/src/ieee754.c
@@ -182,6 +182,12 @@
     return f;
 }
 
+static inline double CopyUint64ToDouble(uint64_t u64)
+{
+    double d;
+    memcpy(&d, &u64, sizeof(uint64_t));
+    return d;
+}
 
 
 // Public function; see ieee754.h
@@ -379,10 +385,65 @@
 }
 
 
-/*
- double IEEE754_HalfToDouble(uint16_t uHalfPrecision) is not needed
-*/
-
+// Public function; see ieee754.h
+double IEEE754_HalfToDouble(uint16_t uHalfPrecision)
+{
+    // Pull out the three parts of the half-precision float
+    const uint16_t uHalfSignificand      =   uHalfPrecision & HALF_SIGNIFICAND_MASK;
+    const int16_t  nHalfUnBiasedExponent = ((uHalfPrecision & HALF_EXPONENT_MASK) >> HALF_EXPONENT_SHIFT) - HALF_EXPONENT_BIAS;
+    const uint16_t uHalfSign             =  (uHalfPrecision & HALF_SIGN_MASK) >> HALF_SIGN_SHIFT;
+    
+    
+    // Make the three parts of hte single-precision number
+    uint64_t uDoubleSignificand, uDoubleSign, uDoubleBiasedExponent;
+    if(nHalfUnBiasedExponent == HALF_EXPONENT_ZERO) {
+        // 0 or subnormal
+        uDoubleBiasedExponent = DOUBLE_EXPONENT_ZERO + DOUBLE_EXPONENT_BIAS;
+        if(uHalfSignificand) {
+            // Subnormal case
+            uDoubleBiasedExponent = -HALF_EXPONENT_BIAS + DOUBLE_EXPONENT_BIAS +1;
+            // A half-precision subnormal can always be converted to a normal double-precision float because the ranges line up
+            uDoubleSignificand = uHalfSignificand;
+            // Shift bits from right of the decimal to left, reducing the exponent by 1 each time
+            do {
+                uDoubleSignificand <<= 1;
+                uDoubleBiasedExponent--;
+            } while ((uDoubleSignificand & 0x400) == 0);
+            uDoubleSignificand &= HALF_SIGNIFICAND_MASK;
+            uDoubleSignificand <<= (DOUBLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
+        } else {
+            // Just zero
+            uDoubleSignificand = 0;
+        }
+    } else if(nHalfUnBiasedExponent == HALF_EXPONENT_INF_OR_NAN) {
+        // NaN or Inifinity
+        uDoubleBiasedExponent = DOUBLE_EXPONENT_INF_OR_NAN + DOUBLE_EXPONENT_BIAS;
+        if(uHalfSignificand) {
+            // NaN
+            // First preserve the NaN payload from half to single
+            uDoubleSignificand = uHalfSignificand & ~HALF_QUIET_NAN_BIT;
+            if(uHalfSignificand & HALF_QUIET_NAN_BIT) {
+                // Next, set qNaN if needed since half qNaN bit is not copied above
+                uDoubleSignificand |= DOUBLE_QUIET_NAN_BIT;
+            }
+        } else {
+            // Infinity
+            uDoubleSignificand = 0;
+        }
+    } else {
+        // Normal number
+        uDoubleBiasedExponent = nHalfUnBiasedExponent + DOUBLE_EXPONENT_BIAS;
+        uDoubleSignificand    = (uint64_t)uHalfSignificand << (DOUBLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
+    }
+    uDoubleSign = uHalfSign;
+    
+    
+    // Shift the 3 parts into place as a double-precision
+    const uint64_t uDouble = uDoubleSignificand |
+                            (uDoubleBiasedExponent << DOUBLE_EXPONENT_SHIFT) |
+                            (uDoubleSign << DOUBLE_SIGN_SHIFT);
+    return CopyUint64ToDouble(uDouble);
+}
 
 
 // Public function; see ieee754.h
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index e1a0e0c..78ecf9f 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -504,14 +504,16 @@
          break;
            
       case HALF_PREC_FLOAT:
-         pDecodedItem->val.fnum  = IEEE754_HalfToFloat((uint16_t)uNumber);
-         pDecodedItem->uDataType = QCBOR_TYPE_FLOAT;
+         pDecodedItem->val.dfnum = IEEE754_HalfToDouble((uint16_t)uNumber);
+         pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
          break;
       case SINGLE_PREC_FLOAT:
-         pDecodedItem->val.fnum = UsefulBufUtil_CopyUint32ToFloat((uint32_t)uNumber);
+         pDecodedItem->val.dfnum = (double)UsefulBufUtil_CopyUint32ToFloat((uint32_t)uNumber);
+         pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
          break;
       case DOUBLE_PREC_FLOAT:
          pDecodedItem->val.dfnum = UsefulBufUtil_CopyUint64ToDouble(uNumber);
+         pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
          break;
          
       case CBOR_SIMPLEV_FALSE: // 20
@@ -605,7 +607,7 @@
    }
    UsefulBufC Temp          = pDecodedItem->val.string;
    pDecodedItem->val.bigNum = Temp;
-   pDecodedItem->uDataType  = pDecodedItem->uTagBits & QCBOR_TAGFLAG_POS_BIGNUM ? QCBOR_TYPE_POSBIGNUM : QCBOR_TYPE_NEGBIGNUM; // TODO: check this
+   pDecodedItem->uDataType  = pDecodedItem->uTagBits & QCBOR_TAGFLAG_POS_BIGNUM ? QCBOR_TYPE_POSBIGNUM : QCBOR_TYPE_NEGBIGNUM;
    return QCBOR_SUCCESS;
 }
 
@@ -619,7 +621,6 @@
    int nReturn = QCBOR_SUCCESS;
    
    pDecodedItem->val.epochDate.fSecondsFraction = 0;
-   double d = pDecodedItem->val.dfnum; // Might not use this, but keeps code flow neater below
    
    switch (pDecodedItem->uDataType) {
          
@@ -635,17 +636,16 @@
          pDecodedItem->val.epochDate.nSeconds = pDecodedItem->val.uint64;
          break;
          
-      case QCBOR_TYPE_FLOAT:
-         d = pDecodedItem->val.fnum;
-         // Fall through
-         
       case QCBOR_TYPE_DOUBLE:
-         if(d > INT64_MAX) {
-            nReturn = QCBOR_ERR_DATE_OVERFLOW;
-            goto Done;
+         {
+            const double d = pDecodedItem->val.dfnum;
+            if(d > INT64_MAX) {
+               nReturn = QCBOR_ERR_DATE_OVERFLOW;
+               goto Done;
+            }
+            pDecodedItem->val.epochDate.nSeconds = d; // Float to integer conversion happening here.
+            pDecodedItem->val.epochDate.fSecondsFraction = d - pDecodedItem->val.epochDate.nSeconds;
          }
-         pDecodedItem->val.epochDate.nSeconds = d; // Float to integer conversion happening here.
-         pDecodedItem->val.epochDate.fSecondsFraction = d - pDecodedItem->val.epochDate.nSeconds;
          break;
          
       default:
diff --git a/test/float_tests.c b/test/float_tests.c
index a1c8c8d..c9861bc 100644
--- a/test/float_tests.c
+++ b/test/float_tests.c
@@ -105,87 +105,88 @@
     }
 
     QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0F) {
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0F) {
         return -2;
     }
     
     QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != INFINITY) {
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
         return -3;
     }
 
     QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != -INFINITY) {
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -INFINITY) {
         return -4;
     }
 
     QCBORDecode_GetNext(&DC, &Item); // TODO, is this really converting right? It is carrying payload, but this confuses things.
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || !isnan(Item.val.fnum)) {
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || !isnan(Item.val.dfnum)) {
         return -5;
     }
 
     QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 1.0F) {
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 1.0F) {
         return -6;
     }
     
     QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.333251953125F) {
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.333251953125F) {
         return -7;
     }
 
     QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 65504.0F) {
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 65504.0F) {
         return -8;
     }
 
     QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != INFINITY) {
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
         return -9;
     }
     
     QCBORDecode_GetNext(&DC, &Item); // TODO: check this
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000000596046448F) {
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000000596046448F) {
         return -10;
     }
 
     QCBORDecode_GetNext(&DC, &Item); // TODO: check this
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000609755516F) {
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000609755516F) {
         return -11;
     }
 
     QCBORDecode_GetNext(&DC, &Item); // TODO check this
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0.0000610351563F) {
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000610351563F) {
         return -12;
     }
     
     QCBORDecode_GetNext(&DC, &Item); 
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != 0) {
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0) {
         return -13;
     }
     
     QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || Item.val.fnum != -2.0F) {
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -2.0F) {
         return -14;
     }
-
-    QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7fc00000L) {
+    
+    // TODO: double check these four tests
+    QCBORDecode_GetNext(&DC, &Item); // qNaN
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff8000000000000ULL) {
         return -15;
     }
-    QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7f800001) {
+    QCBORDecode_GetNext(&DC, &Item); // sNaN
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff0000000000001ULL) {
         return -16;
     }
-    QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7fc0000f) {
+    QCBORDecode_GetNext(&DC, &Item); // qNaN with payload 0x0f
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff800000000000fULL) {
         return -17;
     }
-    QCBORDecode_GetNext(&DC, &Item);
-    if(Item.uDataType != QCBOR_TYPE_FLOAT || UsefulBufUtil_CopyFloatToUint32(Item.val.fnum) != 0x7f80000f) {
+    QCBORDecode_GetNext(&DC, &Item); // sNaN with payload 0x0f
+    if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff000000000000fULL) {
         return -18;
     }
-    
+
     if(QCBORDecode_Finish(&DC)) {
         return -19;
     }
@@ -220,7 +221,7 @@
         QCBORItem Item;
         
         QCBORDecode_GetNext(&DC, &Item);
-        if(Item.uDataType != QCBOR_TYPE_FLOAT) {
+        if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
             return -1;
         }
         
@@ -230,11 +231,11 @@
             // The RFC code uses the native instructions which may or may not
             // handle sNaN, qNaN and NaN payloads correctly. This test just
             // makes sure it is a NaN and doesn't worry about the type of NaN
-            if(!isnan(Item.val.fnum)) {
+            if(!isnan(Item.val.dfnum)) {
                 return -3;
             }
         } else {
-            if(Item.val.fnum != d) {
+            if(Item.val.dfnum != d) {
                 return -2;
             }
         }
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 2ae641c..70beff5 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -1313,46 +1313,46 @@
       return -1;
    if(Item.uDataType != QCBOR_TYPE_DATE_STRING ||
       UsefulBuf_Compare(Item.val.dateString, UsefulBuf_FromSZ("1985-04-12"))){
-      return -1;
+      return -2;
    }
 
    // Epoch date
    if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return -1;
+      return -3;
    if(Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
       Item.val.epochDate.nSeconds != 1400000000 ||
       Item.val.epochDate.fSecondsFraction != 0 ) {
-      return -1;
+      return -4;
    }
    
    // Epoch date with extra CBOR_TAG_B64 tag that doesn't really mean anything
    // but want to be sure extra tag doesn't cause a problem
    if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return -1;
+      return -5;
    if(Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
       Item.val.epochDate.nSeconds != 1400000001 ||
       Item.val.epochDate.fSecondsFraction != 0 ||
       !QCBORDecode_IsTagged(&DCtx, &Item, CBOR_TAG_B64)) {
-      return -1;
+      return -6;
    }
    
    // Epoch date that is too large for our representation
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_DATE_OVERFLOW) {
-      return -1;
+      return -7;
    }
    
    // Epoch date in float format with fractional seconds
    if((nCBORError = QCBORDecode_GetNext(&DCtx, &Item)))
-      return -1;
+      return -8;
    if(Item.uDataType != QCBOR_TYPE_DATE_EPOCH ||
       Item.val.epochDate.nSeconds != 1 ||
       CHECK_EXPECTED_DOUBLE(Item.val.epochDate.fSecondsFraction, 0.1 )) {
-      return -1;
+      return -9;
    }
    
    // Epoch date float that is too large for our representation
    if(QCBORDecode_GetNext(&DCtx, &Item) != QCBOR_ERR_DATE_OVERFLOW) {
-      return -1;
+      return -10;
    }
    
    // TODO: could use a few more tests with float, double, and half precsion and negative (but coverage is still pretty good)