Bladeren bron

Fix `9.22337e+18 outside range of representable values of type 'long'`

Benoit Blanchon 3 jaren geleden
bovenliggende
commit
ccfbb5fd1d

+ 5 - 0
CHANGELOG.md

@@ -1,6 +1,11 @@
 ArduinoJson: change log
 =======================
 
+HEAD
+----
+
+* Fix `9.22337e+18 is outside the range of representable values of type 'long'`
+
 v6.19.4 (2022-04-05)
 -------
 

+ 1 - 0
extras/tests/MsgPackSerializer/serializeVariant.cpp

@@ -109,6 +109,7 @@ TEST_CASE("serialize MsgPack value") {
 
   SECTION("float 32") {
     checkVariant(1.25, "\xCA\x3F\xA0\x00\x00");
+    checkVariant(9.22337204e+18f, "\xca\x5f\x00\x00\x00");
   }
 
   SECTION("float 64") {

+ 4 - 1
extras/tests/Numbers/CMakeLists.txt

@@ -2,7 +2,10 @@
 # Copyright © 2014-2022, Benoit BLANCHON
 # MIT License
 
-add_executable(NumbersTests 
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED OFF)
+
+add_executable(NumbersTests
 	convertNumber.cpp
 	parseFloat.cpp
 	parseDouble.cpp

+ 58 - 0
extras/tests/Numbers/convertNumber.cpp

@@ -75,4 +75,62 @@ TEST_CASE("canConvertNumber<TOut, TIn>()") {
     CHECK((canConvertNumber<uint16_t, uint8_t>(128)) == true);
     CHECK((canConvertNumber<uint16_t, uint8_t>(255)) == true);
   }
+
+  SECTION("float -> int32_t") {
+    CHECK((canConvertNumber<int32_t, float>(0)) == true);
+    CHECK((canConvertNumber<int32_t, float>(-2.147483904e9f)) == false);
+    CHECK((canConvertNumber<int32_t, float>(-2.147483648e+9f)) == true);
+    CHECK((canConvertNumber<int32_t, float>(2.14748352e+9f)) == true);
+    CHECK((canConvertNumber<int32_t, float>(2.14748365e+9f)) == false);
+  }
+
+  SECTION("double -> int32_t") {
+    CHECK((canConvertNumber<int32_t, double>(0)) == true);
+    CHECK((canConvertNumber<int32_t, double>(-2.147483649e+9)) == false);
+    CHECK((canConvertNumber<int32_t, double>(-2.147483648e+9)) == true);
+    CHECK((canConvertNumber<int32_t, double>(2.147483647e+9)) == true);
+    CHECK((canConvertNumber<int32_t, double>(2.147483648e+9)) == false);
+  }
+
+  SECTION("float -> uint32_t") {
+    CHECK((canConvertNumber<uint32_t, float>(0)) == true);
+    CHECK((canConvertNumber<uint32_t, float>(-1.401298e-45f)) == false);
+    CHECK((canConvertNumber<uint32_t, float>(4.29496704e+9f)) == true);
+    CHECK((canConvertNumber<uint32_t, float>(4.294967296e+9f)) == false);
+  }
+
+#if ARDUINOJSON_HAS_LONG_LONG
+  SECTION("float -> int64_t") {
+    CHECK((canConvertNumber<int64_t, float>(0)) == true);
+    CHECK((canConvertNumber<int64_t, float>(-9.22337204e+18f)) == true);
+    CHECK((canConvertNumber<int64_t, float>(9.22337149e+18f)) == true);
+    CHECK((canConvertNumber<int64_t, float>(9.22337204e+18f)) == false);
+  }
+
+  SECTION("double -> int64_t") {
+    CHECK((canConvertNumber<int64_t, double>(0)) == true);
+    CHECK((canConvertNumber<int64_t, double>(-9.2233720368547758e+18)) == true);
+    CHECK((canConvertNumber<int64_t, double>(9.2233720368547748e+18)) == true);
+    CHECK((canConvertNumber<int64_t, double>(9.2233720368547758e+18)) == false);
+  }
+
+  SECTION("float -> uint64_t") {
+    CHECK((canConvertNumber<uint64_t, float>(0)) == true);
+    CHECK((canConvertNumber<uint64_t, float>(-1.401298e-45f)) == false);
+    CHECK((canConvertNumber<uint64_t, float>(1.844674297419792384e+19f)) ==
+          true);
+    CHECK((canConvertNumber<uint64_t, float>(1.8446744073709551616e+19f)) ==
+          false);
+  }
+
+  SECTION("double -> uint64_t") {
+    CHECK((canConvertNumber<uint64_t, double>(0)) == true);
+    CHECK((canConvertNumber<uint64_t, double>(-4.94065645841247e-324)) ==
+          false);
+    CHECK((canConvertNumber<uint64_t, double>(1.8446744073709549568e+19)) ==
+          true);
+    CHECK((canConvertNumber<uint64_t, double>(1.8446744073709551616e+19)) ==
+          false);
+  }
+#endif
 }

+ 49 - 0
src/ArduinoJson/Numbers/FloatTraits.hpp

@@ -12,6 +12,7 @@
 #include <ArduinoJson/Polyfills/math.hpp>
 #include <ArduinoJson/Polyfills/preprocessor.hpp>
 #include <ArduinoJson/Polyfills/static_array.hpp>
+#include <ArduinoJson/Polyfills/type_traits.hpp>
 
 namespace ARDUINOJSON_NAMESPACE {
 
@@ -116,6 +117,22 @@ struct FloatTraits<T, 8 /*64bits*/> {
     return forge(0x7FEFFFFF, 0xFFFFFFFF);
   }
 
+  template <typename TOut>  // int64_t
+  static T highest_for(
+      typename enable_if<is_integral<TOut>::value && is_signed<TOut>::value &&
+                             sizeof(TOut) == 8,
+                         signed>::type* = 0) {
+    return forge(0x43DFFFFF, 0xFFFFFFFF);  //  9.2233720368547748e+18
+  }
+
+  template <typename TOut>  // uint64_t
+  static T highest_for(
+      typename enable_if<is_integral<TOut>::value && is_unsigned<TOut>::value &&
+                             sizeof(TOut) == 8,
+                         unsigned>::type* = 0) {
+    return forge(0x43EFFFFF, 0xFFFFFFFF);  //  1.8446744073709549568e+19
+  }
+
   static T lowest() {
     return forge(0xFFEFFFFF, 0xFFFFFFFF);
   }
@@ -212,6 +229,38 @@ struct FloatTraits<T, 4 /*32bits*/> {
     return forge(0x7f7fffff);
   }
 
+  template <typename TOut>  // int32_t
+  static T highest_for(
+      typename enable_if<is_integral<TOut>::value && is_signed<TOut>::value &&
+                             sizeof(TOut) == 4,
+                         signed>::type* = 0) {
+    return forge(0x4EFFFFFF);  // 2.14748352E9
+  }
+
+  template <typename TOut>  // uint32_t
+  static T highest_for(
+      typename enable_if<is_integral<TOut>::value && is_unsigned<TOut>::value &&
+                             sizeof(TOut) == 4,
+                         unsigned>::type* = 0) {
+    return forge(0x4F7FFFFF);  // 4.29496704E9
+  }
+
+  template <typename TOut>  // int64_t
+  static T highest_for(
+      typename enable_if<is_integral<TOut>::value && is_signed<TOut>::value &&
+                             sizeof(TOut) == 8,
+                         signed>::type* = 0) {
+    return forge(0x5EFFFFFF);  // 9.22337148709896192E18
+  }
+
+  template <typename TOut>  // uint64_t
+  static T highest_for(
+      typename enable_if<is_integral<TOut>::value && is_unsigned<TOut>::value &&
+                             sizeof(TOut) == 8,
+                         unsigned>::type* = 0) {
+    return forge(0x5F7FFFFF);  // 1.844674297419792384E19
+  }
+
   static T lowest() {
     return forge(0xFf7fffff);
   }

+ 22 - 4
src/ArduinoJson/Numbers/convertNumber.hpp

@@ -15,6 +15,7 @@
 #endif
 
 #include <ArduinoJson/Numbers/Float.hpp>
+#include <ArduinoJson/Numbers/FloatTraits.hpp>
 #include <ArduinoJson/Polyfills/limits.hpp>
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 
@@ -95,17 +96,34 @@ canConvertNumber(TIn value) {
   return value <= TIn(numeric_limits<TOut>::highest());
 }
 
-// float -> int32
-// float -> int64
+// float32 -> int16
+// float64 -> int32
 template <typename TOut, typename TIn>
-typename enable_if<is_floating_point<TIn>::value &&
-                       !is_floating_point<TOut>::value,
+typename enable_if<is_floating_point<TIn>::value && is_integral<TOut>::value &&
+                       sizeof(TOut) < sizeof(TIn),
                    bool>::type
 canConvertNumber(TIn value) {
   return value >= numeric_limits<TOut>::lowest() &&
          value <= numeric_limits<TOut>::highest();
 }
 
+// float32 -> int32
+// float32 -> uint32
+// float32 -> int64
+// float32 -> uint64
+// float64 -> int64
+// float64 -> uint64
+template <typename TOut, typename TIn>
+typename enable_if<is_floating_point<TIn>::value && is_integral<TOut>::value &&
+                       sizeof(TOut) >= sizeof(TIn),
+                   bool>::type
+canConvertNumber(TIn value) {
+  // Avoid error "9.22337e+18 is outside the range of representable values of
+  // type 'long'"
+  return value >= numeric_limits<TOut>::lowest() &&
+         value <= FloatTraits<TIn>::template highest_for<TOut>();
+}
+
 template <typename TOut, typename TIn>
 TOut convertNumber(TIn value) {
   return canConvertNumber<TOut>(value) ? TOut(value) : 0;