simpler float decoding to parallel simpler float encoding. Interface uses only a double
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: