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

This commit is contained in:
Benoit Blanchon
2017-07-09 15:24:58 +02:00
parent 788c9be016
commit abfd3997eb
8 changed files with 163 additions and 111 deletions

View File

@ -5,6 +5,9 @@ HEAD
---- ----
* Removed dependency on `PGM_P` as Particle 0.6.2 doesn't define it (issue #546) * 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 v5.11.0
------- -------

View File

@ -148,16 +148,6 @@
#define ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD 1e-5 #define ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD 1e-5
#endif #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 #if ARDUINOJSON_USE_LONG_LONG && ARDUINOJSON_USE_INT64
#error ARDUINOJSON_USE_LONG_LONG and ARDUINOJSON_USE_INT64 cannot be set together #error ARDUINOJSON_USE_LONG_LONG and ARDUINOJSON_USE_INT64 cannot be set together
#endif #endif

View File

@ -8,92 +8,35 @@
#pragma once #pragma once
#include "../Configuration.hpp" #include "../Configuration.hpp"
#include "../TypeTraits/FloatTraits.hpp"
namespace ArduinoJson { namespace ArduinoJson {
namespace Polyfills { namespace Polyfills {
template <typename T> template <typename T>
int16_t normalize(T& value) { int16_t normalize(T& value) {
using namespace TypeTraits;
int16_t powersOf10 = 0; int16_t powersOf10 = 0;
int8_t index = sizeof(T) == 8 ? 8 : 5;
int bit = 1 << index;
if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) { if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) {
#if ARDUINOJSON_DOUBLE_IS_64BITS for (; index >= 0; index--) {
if (value >= 1e256) { if (value >= FloatTraits<T>::positiveBinaryPowerOfTen(index)) {
value /= 1e256; value *= FloatTraits<T>::negativeBinaryPowerOfTen(index);
powersOf10 = int16_t(powersOf10 + 256); powersOf10 = int16_t(powersOf10 + bit);
} }
if (value >= 1e128) { bit >>= 1;
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);
} }
} }
if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) { if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) {
#if ARDUINOJSON_DOUBLE_IS_64BITS for (; index >= 0; index--) {
if (value < 1e-255) { if (value < FloatTraits<T>::negativeBinaryPowerOfTenPlusOne(index)) {
value *= 1e256; value *= FloatTraits<T>::positiveBinaryPowerOfTen(index);
powersOf10 = int16_t(powersOf10 - 256); powersOf10 = int16_t(powersOf10 - bit);
} }
if (value < 1e-127) { bit >>= 1;
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);
} }
} }

View File

@ -18,7 +18,6 @@ namespace TypeTraits {
template <typename T, size_t = sizeof(T)> template <typename T, size_t = sizeof(T)>
struct FloatTraits {}; struct FloatTraits {};
#if ARDUINOJSON_DOUBLE_IS_64BITS
template <typename T> template <typename T>
struct FloatTraits<T, 8 /*64bits*/> { struct FloatTraits<T, 8 /*64bits*/> {
typedef int64_t mantissa_type; typedef int64_t mantissa_type;
@ -31,29 +30,65 @@ struct FloatTraits<T, 8 /*64bits*/> {
template <typename TExponent> template <typename TExponent>
static T make_float(T m, TExponent e) { static T make_float(T m, TExponent e) {
if (e >= 0) if (e > 0) {
return m * (e & 1 ? 1e1 : 1) * (e & 2 ? 1e2 : 1) * (e & 4 ? 1e4 : 1) * for (uint8_t index = 0; e != 0; index++) {
(e & 8 ? 1e8 : 1) * (e & 16 ? 1e16 : 1) * (e & 32 ? 1e32 : 1) * if (e & 1) m *= positiveBinaryPowerOfTen(index);
(e & 64 ? 1e64 : 1) * (e & 128 ? 1e128 : 1) * e >>= 1;
(e & 256 ? 1e256 : 1); }
e = TExponent(-e); } else {
return m * (e & 1 ? 1e-1 : 1) * (e & 2 ? 1e-2 : 1) * (e & 4 ? 1e-4 : 1) * e = TExponent(-e);
(e & 8 ? 1e-8 : 1) * (e & 16 ? 1e-16 : 1) * (e & 32 ? 1e-32 : 1) * for (uint8_t index = 0; e != 0; index++) {
(e & 64 ? 1e-64 : 1) * (e & 128 ? 1e-128 : 1) * if (e & 1) m *= negativeBinaryPowerOfTen(index);
(e & 256 ? 1e-256 : 1); 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() { static T nan() {
uint64_t x = uint64_t(0x7ff8) << 48; return forge(0x7ff80000, 0x00000000);
return *reinterpret_cast<T*>(&x);
} }
static T inf() { static T inf() {
uint64_t x = uint64_t(0x7ff0) << 48; return forge(0x7ff00000, 0x00000000);
return *reinterpret_cast<T*>(&x); }
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> template <typename T>
struct FloatTraits<T, 4 /*32bits*/> { struct FloatTraits<T, 4 /*32bits*/> {
@ -67,22 +102,51 @@ struct FloatTraits<T, 4 /*32bits*/> {
template <typename TExponent> template <typename TExponent>
static T make_float(T m, TExponent e) { static T make_float(T m, TExponent e) {
if (e > 0) if (e > 0) {
return m * (e & 1 ? 1e1f : 1) * (e & 2 ? 1e2f : 1) * (e & 4 ? 1e4f : 1) * for (uint8_t index = 0; e != 0; index++) {
(e & 8 ? 1e8f : 1) * (e & 16 ? 1e16f : 1) * (e & 32 ? 1e32f : 1); if (e & 1) m *= positiveBinaryPowerOfTen(index);
e = -e; e >>= 1;
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); } 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() { static T nan() {
uint32_t x = 0x7fc00000; return forge(0x7fc00000);
return *reinterpret_cast<T*>(&x);
} }
static T inf() { static T inf() {
uint32_t x = 0x7f800000; return forge(0x7f800000);
return *reinterpret_cast<T*>(&x);
} }
}; };
} }

View File

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

View File

@ -10,5 +10,12 @@ add_executable(IntegrationTests
round_trip.cpp 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) target_link_libraries(IntegrationTests catch)
add_test(IntegrationTests IntegrationTests) add_test(IntegrationTests IntegrationTests)

View File

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

View File

@ -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.17549435e38") {
float value = 1.17549435e-38f;
int exp = normalize(value);
REQUIRE(value == Approx(1.17549435));
REQUIRE(exp == -38);
}
}