more thorough removal of float
diff --git a/src/ieee754.c b/src/ieee754.c
index 41f60cf..ac454b4 100644
--- a/src/ieee754.c
+++ b/src/ieee754.c
@@ -10,6 +10,8 @@
Created on 7/23/18
=============================================================================*/
+#ifndef QCBOR_CONFIG_DISABLE_ENCODE_IEEE754
+
#include "ieee754.h"
#include <string.h> // For memcpy()
@@ -391,8 +393,8 @@
double IEEE754_HalfToDouble(uint16_t uHalfPrecision)
{
// Pull out the three parts of the half-precision float
- // Do all the work in 64 bits because that is what the end result is
- // may give smaller code size and will keep static analyzers happier.
+ // Do all the work in 64 bits because that is what the end result is.
+ // It may give smaller code size and will keep static analyzers happier.
const uint64_t uHalfSignificand = uHalfPrecision & HALF_SIGNIFICAND_MASK;
const int64_t nHalfUnBiasedExponent = (int64_t)((uHalfPrecision & HALF_EXPONENT_MASK) >> HALF_EXPONENT_SHIFT) - HALF_EXPONENT_BIAS;
const uint64_t uHalfSign = (uHalfPrecision & HALF_SIGN_MASK) >> HALF_SIGN_SHIFT;
@@ -451,6 +453,74 @@
// Public function; see ieee754.h
+double IEEE754_FloatToDouble(float f)
+{
+ const uint32_t uFloat = CopyFloatToUint32(f);
+ // Pull out the three parts of the single-precision float
+ // Do all the work in 64 bits because that is what the end result is.
+ // It may give smaller code size and will keep static analyzers happier.
+ const uint64_t uSingleSignificand = uFloat & SINGLE_SIGNIFICAND_MASK;
+ const int64_t nSingleUnBiasedExponent = (int64_t)((uFloat & SINGLE_EXPONENT_MASK) >> SINGLE_EXPONENT_SHIFT) - SINGLE_EXPONENT_BIAS;
+ const uint64_t uSingleSign = (uFloat & SINGLE_SIGN_MASK) >> SINGLE_SIGN_SHIFT;
+
+
+ // Make the three parts of hte single-precision number
+ uint64_t uDoubleSignificand, uDoubleSign, uDoubleBiasedExponent;
+ if(nSingleUnBiasedExponent == SINGLE_EXPONENT_ZERO) {
+ // 0 or subnormal
+ uDoubleBiasedExponent = DOUBLE_EXPONENT_ZERO + DOUBLE_EXPONENT_BIAS;
+ if(uSingleSignificand) {
+ // Subnormal case
+ uDoubleBiasedExponent = -SINGLE_EXPONENT_BIAS + DOUBLE_EXPONENT_BIAS + 1;
+ // A single-precision subnormal can always be converted to a normal double-precision float because the ranges line up
+ uDoubleSignificand = uSingleSignificand;
+ // Shift bits from right of the decimal to left, reducing the exponent by 1 each time
+ do {
+ uDoubleSignificand <<= 1;
+ uDoubleBiasedExponent--;
+ // TODO: is this right? Where does 0x400 come from?
+ } while ((uDoubleSignificand & 0x400) == 0);
+ uDoubleSignificand &= SINGLE_SIGNIFICAND_MASK;
+ uDoubleSignificand <<= (DOUBLE_NUM_SIGNIFICAND_BITS - SINGLE_NUM_SIGNIFICAND_BITS);
+ } else {
+ // Just zero
+ uDoubleSignificand = 0;
+ }
+ } else if(nSingleUnBiasedExponent == SINGLE_EXPONENT_INF_OR_NAN) {
+ // NaN or Inifinity
+ uDoubleBiasedExponent = DOUBLE_EXPONENT_INF_OR_NAN + DOUBLE_EXPONENT_BIAS;
+ if(uSingleSignificand) {
+ // NaN
+ // First preserve the NaN payload from half to single
+ // TODO: check this
+ uDoubleSignificand = uSingleSignificand & ~SINGLE_QUIET_NAN_BIT;
+ if(uSingleSignificand & SINGLE_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 = (uint64_t)(nSingleUnBiasedExponent + DOUBLE_EXPONENT_BIAS);
+ uDoubleSignificand = uSingleSignificand << (DOUBLE_NUM_SIGNIFICAND_BITS - SINGLE_NUM_SIGNIFICAND_BITS);
+ }
+ uDoubleSign = uSingleSign;
+
+
+ // 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
IEEE754_union IEEE754_FloatToSmallest(float f)
{
IEEE754_union result;
@@ -526,3 +596,4 @@
return result;
}
+#endif /* QCBOR_CONFIG_DISABLE_ENCODE_IEEE754 */
diff --git a/src/ieee754.h b/src/ieee754.h
index 705ef62..47bfea5 100644
--- a/src/ieee754.h
+++ b/src/ieee754.h
@@ -10,6 +10,8 @@
Created on 7/23/18
=============================================================================*/
+#ifndef QCBOR_CONFIG_DISABLE_ENCODE_IEEE754
+
#ifndef ieee754_h
#define ieee754_h
@@ -96,6 +98,13 @@
double IEEE754_HalfToDouble(uint16_t uHalfPrecision);
+/*
+ Convert float to double-precision without using any
+ floating-point HW or compiler-supplied SW.
+ This is a loss-less conversion.
+ */
+double IEEE754_FloatToDouble(float f);
+
// Both tags the value and gives the size
#define IEEE754_UNION_IS_HALF 2
@@ -146,7 +155,7 @@
#endif /* ieee754_h */
-
+#endif /* QCBOR_CONFIG_DISABLE_ENCODE_IEEE754 */
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 1b6ff3e..532da1e 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -608,12 +608,24 @@
// caught before this is called.
case HALF_PREC_FLOAT:
+#ifndef QCBOR_CONFIG_DISABLE_ENCODE_IEEE754
+ // The caast to uint16_t is safe because the encoded value
+ // was 16 bits. It was widened to 64 bits to be passed in here.
pDecodedItem->val.dfnum = IEEE754_HalfToDouble((uint16_t)uNumber);
pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
+#else
+ nReturn = QCBOR_ERR_UNSUPPORTED;
+#endif
break;
case SINGLE_PREC_FLOAT:
- pDecodedItem->val.dfnum = (double)UsefulBufUtil_CopyUint32ToFloat((uint32_t)uNumber);
- pDecodedItem->uDataType = QCBOR_TYPE_DOUBLE;
+#ifndef QCBOR_CONFIG_DISABLE_ENCODE_IEEE754
+ // The caast to uint32_t is safe because the encoded value
+ // was 16 bits. It was widened to 64 bits to be passed in here.
+ pDecodedItem->val.dfnum = IEEE754_FloatToDouble((uint32_t)uNumber);
+#else
+ pDecodedItem->val.fnum = UsefulBufUtil_CopyUint32ToFloat((uint32_t)uNumber);
+ pDecodedItem->uDataType = QCBOR_TYPE_FLOAT;
+#endif
break;
case DOUBLE_PREC_FLOAT:
pDecodedItem->val.dfnum = UsefulBufUtil_CopyUint64ToDouble(uNumber);
@@ -1221,7 +1233,10 @@
pDecodedItem->val.epochDate.nSeconds = (int64_t)pDecodedItem->val.uint64;
break;
+ // TODO: test this with float and half input
case QCBOR_TYPE_DOUBLE:
+ case QCBOR_TYPE_FLOAT:
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
{
// This comparison needs to be done as a float before
// conversion to an int64_t to be able to detect doubles
@@ -1231,7 +1246,7 @@
// is bad and wrong for the comparison because it will
// allow conversion of doubles that can't fit into a
// uint64_t. To remedy this INT64_MAX - 0x7ff is used as
- // the cutoff point as if that rounds up in conversion to
+ // the cutoff point because if that value rounds up in conversion to
// double it will still be less than INT64_MAX. 0x7ff is
// picked because it has 11 bits set.
//
@@ -1242,7 +1257,8 @@
//
// Without the 0x7ff there is a ~30 minute range of time
// values 10 billion years in the past and in the future
- // where this this code would go wrong.
+ // where this this code would go wrong and some compilers
+ // will generate warnings or errors.
const double d = pDecodedItem->val.dfnum;
if(d > (double)(INT64_MAX - 0x7ff)) {
nReturn = QCBOR_ERR_DATE_OVERFLOW;
@@ -1251,8 +1267,18 @@
pDecodedItem->val.epochDate.nSeconds = (int64_t)d;
pDecodedItem->val.epochDate.fSecondsFraction = d - (double)pDecodedItem->val.epochDate.nSeconds;
}
+#else
+ /* Disabling float support causes a floating point
+ data to error in the default below. The above code
+ requires floating point conversion to integers and
+ comparison which requires either floating point HW
+ or a SW library. */
+
+ nReturn = QCBOR_ERR_FLOAT_DATE_UNSUPPORTED;
+#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
break;
+
default:
nReturn = QCBOR_ERR_BAD_OPT_TAG;
goto Done;
diff --git a/src/qcbor_encode.c b/src/qcbor_encode.c
index 5cc0bd7..5536194 100644
--- a/src/qcbor_encode.c
+++ b/src/qcbor_encode.c
@@ -616,9 +616,29 @@
*/
void QCBOREncode_AddDouble(QCBOREncodeContext *me, double dNum)
{
+#ifndef QCBOR_CONFIG_DISABLE_ENCODE_IEEE754
const IEEE754_union uNum = IEEE754_DoubleToSmallest(dNum);
-
+
QCBOREncode_AddType7(me, uNum.uSize, uNum.uValue);
+#else
+ QCBOREncode_AddType7(me, sizeof(uint64_t), UsefulBufUtil_CopyDoubleToUint64(dNum));
+#endif
+
+}
+
+
+/*
+ Public functions for closing arrays and maps. See qcbor.h
+ */
+void QCBOREncode_AddFloat(QCBOREncodeContext *me, float fNum)
+{
+#ifndef QCBOR_CONFIG_DISABLE_ENCODE_IEEE754
+ const IEEE754_union uNum = IEEE754_FloatToSmallest(fNum);
+
+ QCBOREncode_AddType7(me, uNum.uSize, uNum.uValue);
+#else
+ QCBOREncode_AddType7(me, sizeof(uint32_t), UsefulBufUtil_CopyFloatToUint32(fNum));
+#endif
}