Преглед изворни кода

Fixed "constant exceeds range of float [-Woverflow]" (issue #544)

Benoit Blanchon пре 8 година
родитељ
комит
abfd3997eb

+ 3 - 0
CHANGELOG.md

@@ -5,6 +5,9 @@ HEAD
 ----
 
 * Removed dependency on `PGM_P` as Particle 0.6.2 doesn't define it (issue #546)
+* Fixed warning "dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]"
+* Fixed warning "floating constant exceeds range of 'float' [-Woverflow]" (issue #544)
+* Removed `ARDUINOJSON_DOUBLE_IS_64BITS` as it became useless.
 
 v5.11.0
 -------

+ 0 - 10
src/ArduinoJson/Configuration.hpp

@@ -148,16 +148,6 @@
 #define ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD 1e-5
 #endif
 
-// how many bits in a double
-#ifndef ARDUINOJSON_DOUBLE_IS_64BITS
-#if /*GCC*/ (defined(__SIZEOF_DOUBLE__) && __SIZEOF_DOUBLE__ < 8) || \
-    /*IAR*/ (defined(__DOUBLE__) && __DOUBLE__ < 64)
-#define ARDUINOJSON_DOUBLE_IS_64BITS 0
-#else
-#define ARDUINOJSON_DOUBLE_IS_64BITS 1  // by default support 64-bit
-#endif
-#endif
-
 #if ARDUINOJSON_USE_LONG_LONG && ARDUINOJSON_USE_INT64
 #error ARDUINOJSON_USE_LONG_LONG and ARDUINOJSON_USE_INT64 cannot be set together
 #endif

+ 17 - 74
src/ArduinoJson/Polyfills/normalize.hpp

@@ -8,92 +8,35 @@
 #pragma once
 
 #include "../Configuration.hpp"
+#include "../TypeTraits/FloatTraits.hpp"
 
 namespace ArduinoJson {
 namespace Polyfills {
 template <typename T>
 int16_t normalize(T& value) {
+  using namespace TypeTraits;
   int16_t powersOf10 = 0;
 
+  int8_t index = sizeof(T) == 8 ? 8 : 5;
+  int bit = 1 << index;
+
   if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) {
-#if ARDUINOJSON_DOUBLE_IS_64BITS
-    if (value >= 1e256) {
-      value /= 1e256;
-      powersOf10 = int16_t(powersOf10 + 256);
-    }
-    if (value >= 1e128) {
-      value /= 1e128;
-      powersOf10 = int16_t(powersOf10 + 128);
-    }
-    if (value >= 1e64) {
-      value /= 1e64;
-      powersOf10 = int16_t(powersOf10 + 64);
-    }
-#endif
-    if (value >= 1e32) {
-      value /= 1e32;
-      powersOf10 = int16_t(powersOf10 + 32);
-    }
-    if (value >= 1e16) {
-      value /= 1e16;
-      powersOf10 = int16_t(powersOf10 + 16);
-    }
-    if (value >= 1e8) {
-      value /= 1e8;
-      powersOf10 = int16_t(powersOf10 + 8);
-    }
-    if (value >= 1e4) {
-      value /= 1e4;
-      powersOf10 = int16_t(powersOf10 + 4);
-    }
-    if (value >= 1e2) {
-      value /= 1e2;
-      powersOf10 = int16_t(powersOf10 + 2);
-    }
-    if (value >= 1e1) {
-      value /= 1e1;
-      powersOf10 = int16_t(powersOf10 + 1);
+    for (; index >= 0; index--) {
+      if (value >= FloatTraits<T>::positiveBinaryPowerOfTen(index)) {
+        value *= FloatTraits<T>::negativeBinaryPowerOfTen(index);
+        powersOf10 = int16_t(powersOf10 + bit);
+      }
+      bit >>= 1;
     }
   }
 
   if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) {
-#if ARDUINOJSON_DOUBLE_IS_64BITS
-    if (value < 1e-255) {
-      value *= 1e256;
-      powersOf10 = int16_t(powersOf10 - 256);
-    }
-    if (value < 1e-127) {
-      value *= 1e128;
-      powersOf10 = int16_t(powersOf10 - 128);
-    }
-    if (value < 1e-63) {
-      value *= 1e64;
-      powersOf10 = int16_t(powersOf10 - 64);
-    }
-#endif
-    if (value < 1e-31) {
-      value *= 1e32;
-      powersOf10 = int16_t(powersOf10 - 32);
-    }
-    if (value < 1e-15) {
-      value *= 1e16;
-      powersOf10 = int16_t(powersOf10 - 16);
-    }
-    if (value < 1e-7) {
-      value *= 1e8;
-      powersOf10 = int16_t(powersOf10 - 8);
-    }
-    if (value < 1e-3) {
-      value *= 1e4;
-      powersOf10 = int16_t(powersOf10 - 4);
-    }
-    if (value < 1e-1) {
-      value *= 1e2;
-      powersOf10 = int16_t(powersOf10 - 2);
-    }
-    if (value < 1e0) {
-      value *= 1e1;
-      powersOf10 = int16_t(powersOf10 - 1);
+    for (; index >= 0; index--) {
+      if (value < FloatTraits<T>::negativeBinaryPowerOfTenPlusOne(index)) {
+        value *= FloatTraits<T>::positiveBinaryPowerOfTen(index);
+        powersOf10 = int16_t(powersOf10 - bit);
+      }
+      bit >>= 1;
     }
   }
 

+ 90 - 26
src/ArduinoJson/TypeTraits/FloatTraits.hpp

@@ -18,7 +18,6 @@ namespace TypeTraits {
 template <typename T, size_t = sizeof(T)>
 struct FloatTraits {};
 
-#if ARDUINOJSON_DOUBLE_IS_64BITS
 template <typename T>
 struct FloatTraits<T, 8 /*64bits*/> {
   typedef int64_t mantissa_type;
@@ -31,29 +30,65 @@ struct FloatTraits<T, 8 /*64bits*/> {
 
   template <typename TExponent>
   static T make_float(T m, TExponent e) {
-    if (e >= 0)
-      return m * (e & 1 ? 1e1 : 1) * (e & 2 ? 1e2 : 1) * (e & 4 ? 1e4 : 1) *
-             (e & 8 ? 1e8 : 1) * (e & 16 ? 1e16 : 1) * (e & 32 ? 1e32 : 1) *
-             (e & 64 ? 1e64 : 1) * (e & 128 ? 1e128 : 1) *
-             (e & 256 ? 1e256 : 1);
-    e = TExponent(-e);
-    return m * (e & 1 ? 1e-1 : 1) * (e & 2 ? 1e-2 : 1) * (e & 4 ? 1e-4 : 1) *
-           (e & 8 ? 1e-8 : 1) * (e & 16 ? 1e-16 : 1) * (e & 32 ? 1e-32 : 1) *
-           (e & 64 ? 1e-64 : 1) * (e & 128 ? 1e-128 : 1) *
-           (e & 256 ? 1e-256 : 1);
+    if (e > 0) {
+      for (uint8_t index = 0; e != 0; index++) {
+        if (e & 1) m *= positiveBinaryPowerOfTen(index);
+        e >>= 1;
+      }
+    } else {
+      e = TExponent(-e);
+      for (uint8_t index = 0; e != 0; index++) {
+        if (e & 1) m *= negativeBinaryPowerOfTen(index);
+        e >>= 1;
+      }
+    }
+    return m;
+  }
+
+  static T positiveBinaryPowerOfTen(int index) {
+    static T factors[] = {
+        1e1, 1e2, 1e4, 1e8, 1e16, 1e32,
+        // workaround to support platforms with single precision literals
+        forge(0x4D384F03, 0xE93FF9F5), forge(0x5A827748, 0xF9301D32),
+        forge(0x75154FDD, 0x7F73BF3C)};
+    return factors[index];
+  }
+
+  static T negativeBinaryPowerOfTen(int index) {
+    static T factors[] = {
+        1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32,
+        // workaround to support platforms with single precision literals
+        forge(0x32A50FFD, 0x44F4A73D), forge(0x255BBA08, 0xCF8C979D),
+        forge(0x0AC80628, 0x64AC6F43)};
+    return factors[index];
+  }
+
+  static T negativeBinaryPowerOfTenPlusOne(int index) {
+    static T factors[] = {
+        1e0, 1e-1, 1e-3, 1e-7, 1e-15, 1e-31,
+        // workaround to support platforms with single precision literals
+        forge(0x32DA53FC, 0x9631D10D), forge(0x25915445, 0x81B7DEC2),
+        forge(0x0AFE07B2, 0x7DD78B14)};
+    return factors[index];
   }
 
   static T nan() {
-    uint64_t x = uint64_t(0x7ff8) << 48;
-    return *reinterpret_cast<T*>(&x);
+    return forge(0x7ff80000, 0x00000000);
   }
 
   static T inf() {
-    uint64_t x = uint64_t(0x7ff0) << 48;
-    return *reinterpret_cast<T*>(&x);
+    return forge(0x7ff00000, 0x00000000);
+  }
+
+  static T forge(uint32_t msb, uint32_t lsb) {
+    union {
+      uint64_t integerBits;
+      T floatBits;
+    };
+    integerBits = (uint64_t(msb) << 32) | lsb;
+    return floatBits;
   }
 };
-#endif
 
 template <typename T>
 struct FloatTraits<T, 4 /*32bits*/> {
@@ -67,22 +102,51 @@ struct FloatTraits<T, 4 /*32bits*/> {
 
   template <typename TExponent>
   static T make_float(T m, TExponent e) {
-    if (e > 0)
-      return m * (e & 1 ? 1e1f : 1) * (e & 2 ? 1e2f : 1) * (e & 4 ? 1e4f : 1) *
-             (e & 8 ? 1e8f : 1) * (e & 16 ? 1e16f : 1) * (e & 32 ? 1e32f : 1);
-    e = -e;
-    return m * (e & 1 ? 1e-1f : 1) * (e & 2 ? 1e-2f : 1) * (e & 4 ? 1e-4f : 1) *
-           (e & 8 ? 1e-8f : 1) * (e & 16 ? 1e-16f : 1) * (e & 32 ? 1e-32f : 1);
+    if (e > 0) {
+      for (uint8_t index = 0; e != 0; index++) {
+        if (e & 1) m *= positiveBinaryPowerOfTen(index);
+        e >>= 1;
+      }
+    } else {
+      e = -e;
+      for (uint8_t index = 0; e != 0; index++) {
+        if (e & 1) m *= negativeBinaryPowerOfTen(index);
+        e >>= 1;
+      }
+    }
+    return m;
+  }
+
+  static T positiveBinaryPowerOfTen(int index) {
+    static T factors[] = {1e1f, 1e2f, 1e4f, 1e8f, 1e16f, 1e32f};
+    return factors[index];
+  }
+
+  static T negativeBinaryPowerOfTen(int index) {
+    static T factors[] = {1e-1f, 1e-2f, 1e-4f, 1e-8f, 1e-16f, 1e-32f};
+    return factors[index];
+  }
+
+  static T negativeBinaryPowerOfTenPlusOne(int index) {
+    static T factors[] = {1e0f, 1e-1f, 1e-3f, 1e-7f, 1e-15f, 1e-31f};
+    return factors[index];
+  }
+
+  static T forge(uint32_t bits) {
+    union {
+      uint32_t integerBits;
+      T floatBits;
+    };
+    integerBits = bits;
+    return floatBits;
   }
 
   static T nan() {
-    uint32_t x = 0x7fc00000;
-    return *reinterpret_cast<T*>(&x);
+    return forge(0x7fc00000);
   }
 
   static T inf() {
-    uint32_t x = 0x7f800000;
-    return *reinterpret_cast<T*>(&x);
+    return forge(0x7f800000);
   }
 };
 }

+ 2 - 1
test/CMakeLists.txt

@@ -19,13 +19,14 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
 		-Wformat=2
 		-Winit-self
 		-Wmissing-include-dirs
-		-Wparentheses
 		-Wnon-virtual-dtor
 		-Wold-style-cast
 		-Woverloaded-virtual
+		-Wparentheses
 		-Wredundant-decls
 		-Wshadow
 		-Wsign-promo
+		-Wstrict-aliasing
 		-Wstrict-overflow=5
 		-Wundef
 	)

+ 7 - 0
test/IntegrationTests/CMakeLists.txt

@@ -10,5 +10,12 @@ add_executable(IntegrationTests
 	round_trip.cpp
 )
 
+if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+	target_compile_options(IntegrationTests
+		PUBLIC
+		-fsingle-precision-constant # issue 544
+	)
+endif()
+
 target_link_libraries(IntegrationTests catch)
 add_test(IntegrationTests IntegrationTests)

+ 1 - 0
test/Polyfills/CMakeLists.txt

@@ -8,6 +8,7 @@
 add_executable(PolyfillsTests 
 	isFloat.cpp
 	isInteger.cpp
+	normalize.cpp
 	parseFloat.cpp
 	parseInteger.cpp
 )

+ 43 - 0
test/Polyfills/normalize.cpp

@@ -0,0 +1,43 @@
+// Copyright Benoit Blanchon 2014-2017
+// MIT License
+//
+// Arduino JSON library
+// https://bblanchon.github.io/ArduinoJson/
+// If you like this project, please add a star!
+
+#include <ArduinoJson/Polyfills/normalize.hpp>
+#include <catch.hpp>
+
+using namespace ArduinoJson::Polyfills;
+
+TEST_CASE("normalize<double>()") {
+  SECTION("1.7976931348623157E+308") {
+    double value = 1.7976931348623157E+308;
+    int exp = normalize(value);
+    REQUIRE(value == Approx(1.7976931348623157));
+    REQUIRE(exp == 308);
+  }
+
+  SECTION("4.94065645841247e-324") {
+    double value = 4.94065645841247e-324;
+    int exp = normalize(value);
+    REQUIRE(value == Approx(4.94065645841247));
+    REQUIRE(exp == -324);
+  }
+}
+
+TEST_CASE("normalize<float>()") {
+  SECTION("3.4E+38") {
+    float value = 3.4E+38f;
+    int exp = normalize(value);
+    REQUIRE(value == Approx(3.4f));
+    REQUIRE(exp == 38);
+  }
+
+  SECTION("1.17549435e−38") {
+    float value = 1.17549435e-38f;
+    int exp = normalize(value);
+    REQUIRE(value == Approx(1.17549435));
+    REQUIRE(exp == -38);
+  }
+}