From c4567bac1891e40669eab4b128737760da6ea823 Mon Sep 17 00:00:00 2001 From: Benoit Blanchon Date: Sun, 19 Mar 2017 15:23:06 +0100 Subject: [PATCH] Added custom implementation of `strtod()` (issue #453) --- .github/ISSUE_TEMPLATE.md | 2 +- CHANGELOG.md | 5 + fuzzing/Makefile | 2 +- fuzzing/seed_corpus/Numbers.json | 8 +- include/ArduinoJson/Data/Parse.hpp | 66 ------- include/ArduinoJson/JsonVariantImpl.hpp | 24 +-- include/ArduinoJson/Polyfills/ctype.hpp | 21 +++ include/ArduinoJson/Polyfills/isFloat.hpp | 40 ++++ include/ArduinoJson/Polyfills/math.hpp | 25 ++- include/ArduinoJson/Polyfills/parseFloat.hpp | 87 +++++++++ .../ArduinoJson/Polyfills/parseInteger.hpp | 56 ++++++ .../ArduinoJson/TypeTraits/FloatTraits.hpp | 83 +++++++++ scripts/oss-fuzz/Vagrantfile | 1 + test/Polyfills_IsFloat_Tests.cpp | 81 ++++++++ test/Polyfills_ParseFloat_Tests.cpp | 175 ++++++++++++++++++ 15 files changed, 593 insertions(+), 83 deletions(-) delete mode 100644 include/ArduinoJson/Data/Parse.hpp create mode 100644 include/ArduinoJson/Polyfills/ctype.hpp create mode 100644 include/ArduinoJson/Polyfills/isFloat.hpp create mode 100644 include/ArduinoJson/Polyfills/parseFloat.hpp create mode 100644 include/ArduinoJson/Polyfills/parseInteger.hpp create mode 100644 include/ArduinoJson/TypeTraits/FloatTraits.hpp create mode 100644 test/Polyfills_IsFloat_Tests.cpp create mode 100644 test/Polyfills_ParseFloat_Tests.cpp diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index a803d5cc..f7482378 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -3,7 +3,7 @@ Thanks for using ArduinoJson :-) Before opening an issue, please make sure you've read these: https://bblanchon.github.io/ArduinoJson/faq/ -https://github.com/bblanchon/ArduinoJson/wiki/Avoiding-pitfalls +https://bblanchon.github.io/ArduinoJson/doc/pitfalls/ Next, make sure you provide all the relevant information: platform, code snippet, and error messages. diff --git a/CHANGELOG.md b/CHANGELOG.md index d07a57ce..c2c3f3e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ ArduinoJson: change log ======================= +HEAD +---- + +* Added custom implementation of `strtod()` (issue #453) + v5.8.3 ------ diff --git a/fuzzing/Makefile b/fuzzing/Makefile index 2e90a475..093db806 100644 --- a/fuzzing/Makefile +++ b/fuzzing/Makefile @@ -7,7 +7,7 @@ all: \ $(OUT)/json_fuzzer_seed_corpus.zip \ $(OUT)/json_fuzzer.options -$(OUT)/json_fuzzer: fuzzer.cpp +$(OUT)/json_fuzzer: fuzzer.cpp $(shell find ../include -type f) $(CXX) $(CXXFLAGS) $< -o$@ $(LIB_FUZZING_ENGINE) $(OUT)/json_fuzzer_seed_corpus.zip: seed_corpus/* diff --git a/fuzzing/seed_corpus/Numbers.json b/fuzzing/seed_corpus/Numbers.json index 7ce8ef62..f6b94194 100644 --- a/fuzzing/seed_corpus/Numbers.json +++ b/fuzzing/seed_corpus/Numbers.json @@ -14,5 +14,11 @@ 12.34e+56, 12.34E56, 12.34E-56, - 12.34E+56 + 12.34E+56, + NaN, + -NaN, + +NaN, + Infinity, + +Infinity, + -Infinity ] \ No newline at end of file diff --git a/include/ArduinoJson/Data/Parse.hpp b/include/ArduinoJson/Data/Parse.hpp deleted file mode 100644 index 879f3d2f..00000000 --- a/include/ArduinoJson/Data/Parse.hpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright Benoit Blanchon 2014-2017 -// MIT License -// -// Arduino JSON library -// https://github.com/bblanchon/ArduinoJson -// If you like this project, please add a star! - -#pragma once - -#include - -namespace ArduinoJson { -namespace Internals { -template -TFloat parse(const char *); - -template <> -inline float parse(const char *s) { - return static_cast(strtod(s, NULL)); -} - -template <> -inline double parse(const char *s) { - return strtod(s, NULL); -} - -template <> -inline long parse(const char *s) { - return strtol(s, NULL, 10); -} - -template <> -inline unsigned long parse(const char *s) { - return strtoul(s, NULL, 10); -} - -template <> -inline int parse(const char *s) { - return atoi(s); -} - -#if ARDUINOJSON_USE_LONG_LONG -template <> -inline long long parse(const char *s) { - return strtoll(s, NULL, 10); -} - -template <> -inline unsigned long long parse(const char *s) { - return strtoull(s, NULL, 10); -} -#endif - -#if ARDUINOJSON_USE_INT64 -template <> -inline __int64 parse<__int64>(const char *s) { - return _strtoi64(s, NULL, 10); -} - -template <> -inline unsigned __int64 parse(const char *s) { - return _strtoui64(s, NULL, 10); -} -#endif -} -} diff --git a/include/ArduinoJson/JsonVariantImpl.hpp b/include/ArduinoJson/JsonVariantImpl.hpp index fc41b886..ccf38dbd 100644 --- a/include/ArduinoJson/JsonVariantImpl.hpp +++ b/include/ArduinoJson/JsonVariantImpl.hpp @@ -8,10 +8,12 @@ #pragma once #include "Configuration.hpp" -#include "Data/Parse.hpp" #include "JsonArray.hpp" #include "JsonObject.hpp" #include "JsonVariant.hpp" +#include "Polyfills/isFloat.hpp" +#include "Polyfills/parseFloat.hpp" +#include "Polyfills/parseInteger.hpp" #include // for errno #include // for strtol, strtod @@ -56,14 +58,14 @@ inline Internals::JsonInteger JsonVariant::variantAsInteger() const { case JSON_BOOLEAN: return _content.asInteger; case JSON_NEGATIVE_INTEGER: - return -static_cast(_content.asInteger); + return -static_cast(_content.asInteger); case JSON_STRING: case JSON_UNPARSED: if (!_content.asString) return 0; if (!strcmp("true", _content.asString)) return 1; - return parse(_content.asString); + return Polyfills::parseInteger(_content.asString); default: - return static_cast(_content.asFloat); + return static_cast(_content.asFloat); } } @@ -80,9 +82,9 @@ inline Internals::JsonUInt JsonVariant::asUnsignedInteger() const { case JSON_UNPARSED: if (!_content.asString) return 0; if (!strcmp("true", _content.asString)) return 1; - return parse(_content.asString); + return Polyfills::parseInteger(_content.asString); default: - return static_cast(_content.asFloat); + return static_cast(_content.asFloat); } } @@ -107,7 +109,9 @@ inline Internals::JsonFloat JsonVariant::variantAsFloat() const { return -static_cast(_content.asInteger); case JSON_STRING: case JSON_UNPARSED: - return _content.asString ? parse(_content.asString) : 0; + return _content.asString + ? Polyfills::parseFloat(_content.asString) + : 0; default: return _content.asFloat; } @@ -143,11 +147,7 @@ inline bool JsonVariant::isFloat() const { if (_type != JSON_UNPARSED || _content.asString == NULL) return false; - char *end; - errno = 0; - strtod(_content.asString, &end); - - return *end == '\0' && errno == 0 && !is(); + return Polyfills::isFloat(_content.asString); } #if ARDUINOJSON_ENABLE_STD_STREAM diff --git a/include/ArduinoJson/Polyfills/ctype.hpp b/include/ArduinoJson/Polyfills/ctype.hpp new file mode 100644 index 00000000..63a283d3 --- /dev/null +++ b/include/ArduinoJson/Polyfills/ctype.hpp @@ -0,0 +1,21 @@ +// Copyright Benoit Blanchon 2014-2017 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson +// If you like this project, please add a star! + +#pragma once + +namespace ArduinoJson { +namespace Polyfills { + +inline bool isdigit(char c) { + return '0' <= c && c <= '9'; +} + +inline bool issign(char c) { + return '-' == c || c == '+'; +} +} +} diff --git a/include/ArduinoJson/Polyfills/isFloat.hpp b/include/ArduinoJson/Polyfills/isFloat.hpp new file mode 100644 index 00000000..7557c632 --- /dev/null +++ b/include/ArduinoJson/Polyfills/isFloat.hpp @@ -0,0 +1,40 @@ +// Copyright Benoit Blanchon 2014-2017 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson +// If you like this project, please add a star! + +#pragma once + +#include "./ctype.hpp" +#include "./math.hpp" + +namespace ArduinoJson { +namespace Polyfills { + +inline bool isFloat(const char* s) { + if (!strcmp(s, "NaN")) return true; + if (issign(*s)) s++; + if (!strcmp(s, "Infinity")) return true; + + while (isdigit(*s)) s++; + + bool has_dot = *s == '.'; + if (has_dot) { + s++; + while (isdigit(*s)) s++; + } + + bool has_exponent = *s == 'e' || *s == 'E'; + if (has_exponent) { + s++; + if (issign(*s)) s++; + if (!isdigit(*s)) return false; + while (isdigit(*s)) s++; + } + + return (has_dot || has_exponent) && *s == '\0'; +} +} +} diff --git a/include/ArduinoJson/Polyfills/math.hpp b/include/ArduinoJson/Polyfills/math.hpp index 492936a3..3da29169 100644 --- a/include/ArduinoJson/Polyfills/math.hpp +++ b/include/ArduinoJson/Polyfills/math.hpp @@ -7,10 +7,11 @@ #pragma once -// If Visual Studo <= 2012 -#if defined(_MSC_VER) && _MSC_VER <= 1700 +// If Visual Studo +#if defined(_MSC_VER) #include +#include namespace ArduinoJson { namespace Polyfills { @@ -23,6 +24,16 @@ template bool isInfinity(T x) { return !_finite(x); } + +template +T nan() { + return std::numeric_limits::quiet_NaN(); +} + +template +T inf() { + return std::numeric_limits::infinity(); +} } } @@ -101,6 +112,16 @@ inline bool isInfinity(float x) { } #endif +template +T nan() { + return static_cast(NAN); +} + +template +T inf() { + return static_cast(INFINITY); +} + #if defined(__GNUC__) #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) #pragma GCC diagnostic pop diff --git a/include/ArduinoJson/Polyfills/parseFloat.hpp b/include/ArduinoJson/Polyfills/parseFloat.hpp new file mode 100644 index 00000000..e9c5d98e --- /dev/null +++ b/include/ArduinoJson/Polyfills/parseFloat.hpp @@ -0,0 +1,87 @@ +// Copyright Benoit Blanchon 2014-2017 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson +// If you like this project, please add a star! + +#pragma once + +#include "../TypeTraits/FloatTraits.hpp" +#include "./ctype.hpp" +#include "./math.hpp" + +namespace ArduinoJson { +namespace Polyfills { + +template +inline T parseFloat(const char* s) { + typedef TypeTraits::FloatTraits traits; + typedef typename traits::mantissa_type mantissa_t; + typedef typename traits::exponent_type exponent_t; + + bool negative_result = false; + switch (*s) { + case '-': + negative_result = true; + case '+': + s++; + } + + if (*s == 'n' || *s == 'N') return traits::nan(); + if (*s == 'i' || *s == 'I') + return negative_result ? -traits::inf() : traits::inf(); + + mantissa_t mantissa = 0; + exponent_t exponent_offset = 0; + + while (isdigit(*s)) { + if (mantissa < traits::mantissa_max / 10) + mantissa = mantissa * 10 + (*s - '0'); + else + exponent_offset++; + s++; + } + + if (*s == '.') { + s++; + while (isdigit(*s)) { + if (mantissa < traits::mantissa_max / 10) { + mantissa = mantissa * 10 + (*s - '0'); + exponent_offset--; + } + s++; + } + } + + int exponent = 0; + if (*s == 'e' || *s == 'E') { + s++; + bool negative_exponent = false; + if (*s == '-') { + negative_exponent = true; + s++; + } else if (*s == '+') { + s++; + } + + while (isdigit(*s)) { + exponent = exponent * 10 + (*s - '0'); + if (exponent + exponent_offset > traits::exponent_max) { + if (negative_exponent) + return negative_result ? -0.0f : 0.0f; + else + return negative_result ? -traits::inf() : traits::inf(); + } + s++; + } + if (negative_exponent) exponent = -exponent; + } + exponent += exponent_offset; + + T result = traits::make_float(static_cast(mantissa), exponent); + + return negative_result ? -result : result; +} +} +} diff --git a/include/ArduinoJson/Polyfills/parseInteger.hpp b/include/ArduinoJson/Polyfills/parseInteger.hpp new file mode 100644 index 00000000..90584c4e --- /dev/null +++ b/include/ArduinoJson/Polyfills/parseInteger.hpp @@ -0,0 +1,56 @@ +// Copyright Benoit Blanchon 2014-2017 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson +// If you like this project, please add a star! + +#pragma once + +#include + +namespace ArduinoJson { +namespace Polyfills { +template +T parseInteger(const char *s); + +template <> +inline long parseInteger(const char *s) { + return ::strtol(s, NULL, 10); +} + +template <> +inline unsigned long parseInteger(const char *s) { + return ::strtoul(s, NULL, 10); +} + +template <> +inline int parseInteger(const char *s) { + return ::atoi(s); +} + +#if ARDUINOJSON_USE_LONG_LONG +template <> +inline long long parseInteger(const char *s) { + return ::strtoll(s, NULL, 10); +} + +template <> +inline unsigned long long parseInteger(const char *s) { + return ::strtoull(s, NULL, 10); +} +#endif + +#if ARDUINOJSON_USE_INT64 +template <> +inline __int64 parseInteger<__int64>(const char *s) { + return ::_strtoi64(s, NULL, 10); +} + +template <> +inline unsigned __int64 parseInteger(const char *s) { + return ::_strtoui64(s, NULL, 10); +} +#endif +} +} diff --git a/include/ArduinoJson/TypeTraits/FloatTraits.hpp b/include/ArduinoJson/TypeTraits/FloatTraits.hpp new file mode 100644 index 00000000..9ad398a6 --- /dev/null +++ b/include/ArduinoJson/TypeTraits/FloatTraits.hpp @@ -0,0 +1,83 @@ +// Copyright Benoit Blanchon 2014-2017 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson +// If you like this project, please add a star! + +#pragma once + +#include +#include "../Polyfills/math.hpp" + +namespace ArduinoJson { +namespace TypeTraits { + +template +struct FloatTraits {}; + +#ifndef ARDUINO_ARCH_AVR // double is 32 bits, so 1e64 gives a warning +template +struct FloatTraits { + typedef int64_t mantissa_type; + static const short mantissa_bits = 52; + static const mantissa_type mantissa_max = + (static_cast(1) << mantissa_bits) - 1; + + typedef int16_t exponent_type; + static const exponent_type exponent_max = 308; + + 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 = -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); + } + + static T nan() { + return Polyfills::nan(); + } + + static T inf() { + return Polyfills::inf(); + } +}; +#endif + +template +struct FloatTraits { + typedef int32_t mantissa_type; + static const short mantissa_bits = 23; + static const mantissa_type mantissa_max = + (static_cast(1) << mantissa_bits) - 1; + + typedef int8_t exponent_type; + static const exponent_type exponent_max = 38; + + 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); + } + + static T nan() { + return Polyfills::nan(); + } + + static T inf() { + return Polyfills::inf(); + } +}; +} +} diff --git a/scripts/oss-fuzz/Vagrantfile b/scripts/oss-fuzz/Vagrantfile index ab44af43..252c191c 100644 --- a/scripts/oss-fuzz/Vagrantfile +++ b/scripts/oss-fuzz/Vagrantfile @@ -26,6 +26,7 @@ Vagrant.configure(2) do |config| echo "export PROJECT_NAME='arduinojson'" >> $HOME/.profile echo "export CC='clang'" >> $HOME/.profile echo "export CXX='clang++'" >> $HOME/.profile + echo "export LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/" >> $HOME/.profile echo "Run /host/ArduinoJson/fuzzing/fuzz.sh" | sudo tee /etc/motd SHELL diff --git a/test/Polyfills_IsFloat_Tests.cpp b/test/Polyfills_IsFloat_Tests.cpp new file mode 100644 index 00000000..29dd241a --- /dev/null +++ b/test/Polyfills_IsFloat_Tests.cpp @@ -0,0 +1,81 @@ +// Copyright Benoit Blanchon 2014-2017 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson +// If you like this project, please add a star! + +#include +#include + +using namespace ArduinoJson::Polyfills; + +struct Polyfills_IsFloat_Tests : testing::Test { + void check(bool expected, const char* input) { + bool actual = isFloat(input); + EXPECT_EQ(expected, actual) << input; + } +}; +#define TEST_(X) TEST_F(Polyfills_IsFloat_Tests, X) + +TEST_(NoExponent) { + check(true, "3.14"); + check(true, "-3.14"); + check(true, "+3.14"); +} + +TEST_(IntegralPartMissing) { + check(true, ".14"); + check(true, "-.14"); + check(true, "+.14"); +} + +TEST_(FractionalPartMissing) { + check(true, "3."); + check(true, "-3.e14"); + check(true, "+3.e-14"); +} + +TEST_(NoDot) { + check(true, "3e14"); + check(true, "3e-14"); + check(true, "3e+14"); +} + +TEST_(Integer) { + check(false, "14"); + check(false, "-14"); + check(false, "+14"); +} + +TEST_(ExponentMissing) { + check(false, "3.14e"); + check(false, "3.14e-"); + check(false, "3.14e+"); +} + +TEST_(JustASign) { + check(false, "-"); + check(false, "+"); +} + +TEST_(Empty) { + check(false, ""); +} + +TEST_(NaN) { + check(true, "NaN"); + check(false, "n"); + check(false, "N"); + check(false, "nan"); + check(false, "-NaN"); + check(false, "+NaN"); +} + +TEST_(Infinity) { + check(true, "Infinity"); + check(true, "+Infinity"); + check(true, "-Infinity"); + check(false, "infinity"); + check(false, "Inf"); +} diff --git a/test/Polyfills_ParseFloat_Tests.cpp b/test/Polyfills_ParseFloat_Tests.cpp new file mode 100644 index 00000000..be25e7a8 --- /dev/null +++ b/test/Polyfills_ParseFloat_Tests.cpp @@ -0,0 +1,175 @@ +// Copyright Benoit Blanchon 2014-2017 +// MIT License +// +// Arduino JSON library +// https://github.com/bblanchon/ArduinoJson +// If you like this project, please add a star! + +#include +#include + +using namespace ArduinoJson::Polyfills; + +struct Polyfills_ParseFloat_Float_Tests : testing::Test { + void check(const char* input, float expected) { + float actual = parseFloat(input); + EXPECT_FLOAT_EQ(expected, actual) << input; + } + + void checkNaN(const char* input) { + float result = parseFloat(input); + EXPECT_TRUE(result != result) << input; + } + + void checkInf(const char* input, bool negative) { + float x = parseFloat(input); + if (negative) + EXPECT_TRUE(x < 0) << input; + else + EXPECT_TRUE(x > 0) << input; + EXPECT_TRUE(x == x && x * 2 == x) << input; + } +}; +#define TEST_FLOAT(X) TEST_F(Polyfills_ParseFloat_Float_Tests, X) + +struct Polyfills_ParseFloat_Double_Tests : testing::Test { + void check(const char* input, double expected) { + double actual = parseFloat(input); + EXPECT_DOUBLE_EQ(expected, actual) << input; + } + + void checkNaN(const char* input) { + double result = parseFloat(input); + EXPECT_TRUE(result != result) << input; + } + + void checkInf(const char* input, bool negative) { + double x = parseFloat(input); + if (negative) + EXPECT_TRUE(x < 0) << input; + else + EXPECT_TRUE(x > 0) << input; + EXPECT_TRUE(x == x && x * 2 == x) << input; + } +}; +#define TEST_DOUBLE(X) TEST_F(Polyfills_ParseFloat_Double_Tests, X) + +TEST_DOUBLE(Short_NoExponent) { + check("3.14", 3.14); + check("-3.14", -3.14); + check("+3.14", +3.14); +} + +TEST_FLOAT(Float_Short_NoExponent) { + check("3.14", 3.14f); + check("-3.14", -3.14f); + check("+3.14", +3.14f); +} + +TEST_DOUBLE(Short_NoDot) { + check("1E+308", 1E+308); + check("-1E+308", -1E+308); + check("+1E-308", +1E-308); + check("+1e+308", +1e+308); + check("-1e-308", -1e-308); +} + +TEST_FLOAT(Short_NoDot) { + check("1E+38", 1E+38f); + check("-1E+38", -1E+38f); + check("+1E-38", +1E-38f); + check("+1e+38", +1e+38f); + check("-1e-38", -1e-38f); +} + +TEST_FLOAT(Max) { + check("340.2823e+36", 3.402823e+38f); + check("34.02823e+37", 3.402823e+38f); + check("3.402823e+38", 3.402823e+38f); + check("0.3402823e+39", 3.402823e+38f); + check("00.3402823e+40", 3.402823e+38f); + check("000.3402823e+41", 3.402823e+38f); +} + +TEST_DOUBLE(Max) { + check(".017976931348623147e+310", 1.7976931348623147e+308); + check(".17976931348623147e+309", 1.7976931348623147e+308); + check("1.7976931348623147e+308", 1.7976931348623147e+308); + check("17.976931348623147e+307", 1.7976931348623147e+308); + check("179.76931348623147e+306", 1.7976931348623147e+308); +} + +TEST_DOUBLE(Min) { + check(".022250738585072014e-306", 2.2250738585072014e-308); + check(".22250738585072014e-307", 2.2250738585072014e-308); + check("2.2250738585072014e-308", 2.2250738585072014e-308); + check("22.250738585072014e-309", 2.2250738585072014e-308); + check("222.50738585072014e-310", 2.2250738585072014e-308); +} + +TEST_DOUBLE(VeryLong) { + check("0.00000000000000000000000000000001", 1e-32); + check("100000000000000000000000000000000.0", 1e+32); + check("100000000000000000000000000000000.00000000000000000000000000000", + 1e+32); +} + +TEST_FLOAT(VeryLong) { + check("0.00000000000000000000000000000001", 1e-32f); + check("100000000000000000000000000000000.0", 1e+32f); + check("100000000000000000000000000000000.00000000000000000000000000000", + 1e+32f); +} + +TEST_DOUBLE(MantissaTooLongToFit) { + check("0.179769313486231571111111111111", 0.17976931348623157); + check("17976931348623157.11111111111111", 17976931348623157.0); + check("1797693.134862315711111111111111", 1797693.1348623157); + + check("-0.179769313486231571111111111111", -0.17976931348623157); + check("-17976931348623157.11111111111111", -17976931348623157.0); + check("-1797693.134862315711111111111111", -1797693.1348623157); +} + +TEST_FLOAT(MantissaTooLongToFit) { + check("0.340282346638528861111111111111", 0.34028234663852886f); + check("34028234663852886.11111111111111", 34028234663852886.0f); + check("34028234.66385288611111111111111", 34028234.663852886f); + + check("-0.340282346638528861111111111111", -0.34028234663852886f); + check("-34028234663852886.11111111111111", -34028234663852886.0f); + check("-34028234.66385288611111111111111", -34028234.663852886f); +} + +TEST_DOUBLE(ExponentTooBig) { + checkInf("1e309", false); + checkInf("-1e309", true); + checkInf("1e65535", false); + check("1e-65535", 0.0); +} + +TEST_FLOAT(ExponentTooBig) { + checkInf("1e39", false); + checkInf("-1e39", true); + checkInf("1e255", false); + check("1e-255", 0.0f); +} + +TEST_DOUBLE(NaN) { + checkNaN("NaN"); + checkNaN("nan"); +} + +TEST_FLOAT(NaN) { + checkNaN("NaN"); + checkNaN("nan"); +} + +TEST_FLOAT(Infinity) { + checkInf("Infinity", false); + checkInf("+Infinity", false); + checkInf("-Infinity", true); + checkInf("inf", false); + checkInf("+inf", false); + checkInf("-inf", true); +}