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);