diff --git a/CHANGELOG.md b/CHANGELOG.md index b5a03ed4..1737674a 100644 --- a/CHANGELOG.md +++ b/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 ------- diff --git a/src/ArduinoJson/Configuration.hpp b/src/ArduinoJson/Configuration.hpp index de183914..a1015a6f 100644 --- a/src/ArduinoJson/Configuration.hpp +++ b/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 diff --git a/src/ArduinoJson/Polyfills/normalize.hpp b/src/ArduinoJson/Polyfills/normalize.hpp index 5004b766..a3c25eb4 100644 --- a/src/ArduinoJson/Polyfills/normalize.hpp +++ b/src/ArduinoJson/Polyfills/normalize.hpp @@ -8,92 +8,35 @@ #pragma once #include "../Configuration.hpp" +#include "../TypeTraits/FloatTraits.hpp" namespace ArduinoJson { namespace Polyfills { template 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::positiveBinaryPowerOfTen(index)) { + value *= FloatTraits::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::negativeBinaryPowerOfTenPlusOne(index)) { + value *= FloatTraits::positiveBinaryPowerOfTen(index); + powersOf10 = int16_t(powersOf10 - bit); + } + bit >>= 1; } } diff --git a/src/ArduinoJson/TypeTraits/FloatTraits.hpp b/src/ArduinoJson/TypeTraits/FloatTraits.hpp index 28fa261f..a52e1e87 100644 --- a/src/ArduinoJson/TypeTraits/FloatTraits.hpp +++ b/src/ArduinoJson/TypeTraits/FloatTraits.hpp @@ -18,7 +18,6 @@ namespace TypeTraits { template struct FloatTraits {}; -#if ARDUINOJSON_DOUBLE_IS_64BITS template struct FloatTraits { typedef int64_t mantissa_type; @@ -31,29 +30,65 @@ struct FloatTraits { template 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(&x); + return forge(0x7ff80000, 0x00000000); } static T inf() { - uint64_t x = uint64_t(0x7ff0) << 48; - return *reinterpret_cast(&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 struct FloatTraits { @@ -67,22 +102,51 @@ struct FloatTraits { template 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(&x); + return forge(0x7fc00000); } static T inf() { - uint32_t x = 0x7f800000; - return *reinterpret_cast(&x); + return forge(0x7f800000); } }; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1e34ec32..39f78c57 100644 --- a/test/CMakeLists.txt +++ b/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 ) diff --git a/test/IntegrationTests/CMakeLists.txt b/test/IntegrationTests/CMakeLists.txt index a76660a9..16e42897 100644 --- a/test/IntegrationTests/CMakeLists.txt +++ b/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) diff --git a/test/Polyfills/CMakeLists.txt b/test/Polyfills/CMakeLists.txt index bbc9165c..4800b105 100644 --- a/test/Polyfills/CMakeLists.txt +++ b/test/Polyfills/CMakeLists.txt @@ -8,6 +8,7 @@ add_executable(PolyfillsTests isFloat.cpp isInteger.cpp + normalize.cpp parseFloat.cpp parseInteger.cpp ) diff --git a/test/Polyfills/normalize.cpp b/test/Polyfills/normalize.cpp new file mode 100644 index 00000000..2ebd55f8 --- /dev/null +++ b/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 +#include + +using namespace ArduinoJson::Polyfills; + +TEST_CASE("normalize()") { + 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()") { + 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); + } +}