Fix handling of NaN & Infinity conversion to int using newlib.nano (#314)
* Fix error handling of NaN & Infinity conversion to integer using newlib.nano
* Fix ifdef fan out
* Update package notes
---------
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/pkg/qcbor.spec b/pkg/qcbor.spec
index a9619c6..6bc7bdc 100644
--- a/pkg/qcbor.spec
+++ b/pkg/qcbor.spec
@@ -57,5 +57,6 @@
- Bug fix for QCBORDecode_GetMap() and QCBORDecode_GetArray()
- Fix warning for compilers compliant with C23 standard
- Minor documentation fix
+- Fix for embedded platforms with partial implementations of llround()
* Jan 8 2024 Laurence Lundblade <lgl@island-resort.com> - 1.5.1
- Initial library RPM packaging.
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 402683e..c4bc735 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -5458,6 +5458,7 @@
* @retval QCBOR_ERR_UNEXPECTED_TYPE Of a type that can't be converted
* @retval QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW Conversion result is too large
* or too small.
+ * @retval QCBOR_ERR_FLOAT_EXCEPTION Encountered NaN or infinity or such.
*/
static QCBORError
QCBOR_Private_ConvertInt64(const QCBORItem *pItem,
@@ -5465,33 +5466,68 @@
int64_t *pnValue)
{
switch(pItem->uDataType) {
- case QCBOR_TYPE_FLOAT:
- case QCBOR_TYPE_DOUBLE:
#ifndef QCBOR_DISABLE_FLOAT_HW_USE
- if(uConvertTypes & QCBOR_CONVERT_TYPE_FLOAT) {
- /* https://pubs.opengroup.org/onlinepubs/009695399/functions/llround.html
- http://www.cplusplus.com/reference/cmath/llround/
- */
- // Not interested in FE_INEXACT
- feclearexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW|FE_DIVBYZERO);
- if(pItem->uDataType == QCBOR_TYPE_DOUBLE) {
- *pnValue = llround(pItem->val.dfnum);
- } else {
- *pnValue = lroundf(pItem->val.fnum);
- }
- if(fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW|FE_DIVBYZERO)) {
- // llround() shouldn't result in divide by zero, but catch
- // it here in case it unexpectedly does. Don't try to
- // distinguish between the various exceptions because it seems
- // they vary by CPU, compiler and OS.
- return QCBOR_ERR_FLOAT_EXCEPTION;
- }
- } else {
+ case QCBOR_TYPE_FLOAT:
+ if(!(uConvertTypes & QCBOR_CONVERT_TYPE_FLOAT)) {
return QCBOR_ERR_UNEXPECTED_TYPE;
}
-#else
+ if(isnan(pItem->val.fnum)) {
+ /* In some environments, llround() will succeed on NaN
+ * when it really shouldn't, so catch the error here. */
+ return QCBOR_ERR_FLOAT_EXCEPTION;
+ }
+ if(pItem->val.fnum == INFINITY || pItem->val.fnum == -INFINITY) {
+ return QCBOR_ERR_FLOAT_EXCEPTION;
+ }
+ /* https://pubs.opengroup.org/onlinepubs/009695399/functions/llround.html
+ * http://www.cplusplus.com/reference/cmath/llround/
+ */
+ /* Not interested in FE_INEXACT */
+ feclearexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW|FE_DIVBYZERO);
+ *pnValue = lroundf(pItem->val.fnum);
+ if(fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW|FE_DIVBYZERO)) {
+ /* llround() shouldn't result in divide by zero, but catch
+ * it here in case it unexpectedly does. Don't try to
+ * distinguish between the various exceptions because it seems
+ * they vary by CPU, compiler and OS.
+ */
+ return QCBOR_ERR_FLOAT_EXCEPTION;
+ }
+ break;
+
+ case QCBOR_TYPE_DOUBLE:
+ if(!(uConvertTypes & QCBOR_CONVERT_TYPE_FLOAT)) {
+ return QCBOR_ERR_UNEXPECTED_TYPE;
+ }
+ if(isnan(pItem->val.dfnum)) {
+ /* In some environments, llround() will succeed on NaN
+ * when it really shouldn't, so catch the error here. */
+ return QCBOR_ERR_FLOAT_EXCEPTION;
+ }
+ if(pItem->val.dfnum == INFINITY || pItem->val.dfnum == -INFINITY) {
+ return QCBOR_ERR_FLOAT_EXCEPTION;
+ }
+ /* https://pubs.opengroup.org/onlinepubs/009695399/functions/llround.html
+ * http://www.cplusplus.com/reference/cmath/llround/
+ */
+ /* Not interested in FE_INEXACT */
+ feclearexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW|FE_DIVBYZERO);
+ *pnValue = llround(pItem->val.dfnum);
+ if(fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW|FE_DIVBYZERO)) {
+ /* llround() shouldn't result in divide by zero, but catch
+ * it here in case it unexpectedly does. Don't try to
+ * distinguish between the various exceptions because it seems
+ * they vary by CPU, compiler and OS.
+ */
+ return QCBOR_ERR_FLOAT_EXCEPTION;
+ }
+ break;
+
+#else /* ! QCBOR_DISABLE_FLOAT_HW_USE */
+ case QCBOR_TYPE_FLOAT:
+ case QCBOR_TYPE_DOUBLE:
return QCBOR_ERR_HW_FLOAT_DISABLED;
-#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
+#endif /* ! QCBOR_DISABLE_FLOAT_HW_USE */
break;
case QCBOR_TYPE_INT64:
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 910d43a..86004cb 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -7810,6 +7810,17 @@
INFINITY,
FLOAT_ERR_CODE_NO_FLOAT_HW(QCBOR_SUCCESS)
},
+
+ {
+ "-inifinity single precision",
+ {(uint8_t[]){0xfa, 0xff, 0x80, 0x00, 0x00}, 5},
+ 0,
+ FLOAT_ERR_CODE_NO_FLOAT_HW(QCBOR_ERR_FLOAT_EXCEPTION),
+ 0,
+ FLOAT_ERR_CODE_NO_FLOAT_HW(QCBOR_ERR_NUMBER_SIGN_CONVERSION),
+ -INFINITY,
+ FLOAT_ERR_CODE_NO_FLOAT_HW(QCBOR_SUCCESS)
+ },
};
@@ -7847,7 +7858,13 @@
return (int32_t)(3333+nIndex);
}
+
int64_t nInt;
+
+ if(nIndex == 27) {
+ nInt = 9;
+ }
+
QCBORDecode_GetInt64ConvertAll(&DCtx, 0xffff, &nInt);
if(QCBORDecode_GetError(&DCtx) != pF->uErrorInt64) {
return (int32_t)(2000+nIndex);