mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Fixed some parsing issues in readDoubleValue
This commit is contained in:
parent
837ab64dbd
commit
7c33b2132f
3 changed files with 418 additions and 101 deletions
|
|
@ -1,6 +1,31 @@
|
|||
JUCE breaking changes
|
||||
=====================
|
||||
|
||||
Develop
|
||||
=======
|
||||
|
||||
Change
|
||||
------
|
||||
CharacterFunctions::readDoubleValue now returns values consistent with other
|
||||
C++ number parsing libraries. Parsing values smaller than the minimum number
|
||||
respresentable in a double will return (+/-)0.0 and parsing values larger than
|
||||
the maximum number respresentable in a double will return (+/-)inf.
|
||||
|
||||
Possible Issues
|
||||
---------------
|
||||
Code reading very large or very small numbers may receive values of 0.0 and inf
|
||||
rather than nan.
|
||||
|
||||
Workaround
|
||||
----------
|
||||
Where you may be using std::isnan to check the validity of the result you can
|
||||
instead use std::isfinite.
|
||||
|
||||
Rationale
|
||||
---------
|
||||
The new behaviour is consistent with other string parsing libraries.
|
||||
|
||||
|
||||
Version 6.0.6
|
||||
=============
|
||||
|
||||
|
|
|
|||
|
|
@ -181,19 +181,209 @@ juce_wchar CharacterFunctions::getUnicodeCharFromWindows1252Codepage (const uint
|
|||
|
||||
#define QUOTE(x) #x
|
||||
#define STR(value) QUOTE(value)
|
||||
#define ASYM_STRING_DOUBLE_PAIR(str, value) std::pair<String, double> (STR(str), value)
|
||||
#define STRING_DOUBLE_PAIR(value) ASYM_STRING_DOUBLE_PAIR(value, value)
|
||||
#define STRING_DOUBLE_PAIR_COMBOS(value) \
|
||||
STRING_DOUBLE_PAIR(value), \
|
||||
STRING_DOUBLE_PAIR(-value), \
|
||||
ASYM_STRING_DOUBLE_PAIR(+value, value), \
|
||||
ASYM_STRING_DOUBLE_PAIR(000000 ## value, value), \
|
||||
ASYM_STRING_DOUBLE_PAIR(+000 ## value, value), \
|
||||
ASYM_STRING_DOUBLE_PAIR(-0 ## value, -value)
|
||||
#define ASYM_CHARPTR_DOUBLE_PAIR(str, value) std::pair<const char*, double> (STR(str), value)
|
||||
#define CHARPTR_DOUBLE_PAIR(value) ASYM_CHARPTR_DOUBLE_PAIR(value, value)
|
||||
#define CHARPTR_DOUBLE_PAIR_COMBOS(value) \
|
||||
CHARPTR_DOUBLE_PAIR(value), \
|
||||
CHARPTR_DOUBLE_PAIR(-value), \
|
||||
ASYM_CHARPTR_DOUBLE_PAIR(+value, value), \
|
||||
ASYM_CHARPTR_DOUBLE_PAIR(000000 ## value, value), \
|
||||
ASYM_CHARPTR_DOUBLE_PAIR(+000 ## value, value), \
|
||||
ASYM_CHARPTR_DOUBLE_PAIR(-0 ## value, -value)
|
||||
|
||||
namespace characterFunctionsTests
|
||||
{
|
||||
|
||||
template <typename CharPointerType>
|
||||
MemoryBlock memoryBlockFromCharPtr (const typename CharPointerType::CharType* charPtr)
|
||||
{
|
||||
using CharType = typename CharPointerType::CharType;
|
||||
|
||||
MemoryBlock result;
|
||||
CharPointerType source (charPtr);
|
||||
|
||||
result.setSize (CharPointerType::getBytesRequiredFor (source) + sizeof (CharType));
|
||||
CharPointerType dest { (CharType*) result.getData() };
|
||||
dest.writeAll (source);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename FromCharPointerType, typename ToCharPointerType>
|
||||
MemoryBlock convert (const MemoryBlock& source, bool removeNullTerminator = false)
|
||||
{
|
||||
using ToCharType = typename ToCharPointerType ::CharType;
|
||||
using FromCharType = typename FromCharPointerType::CharType;
|
||||
|
||||
FromCharPointerType sourcePtr { (FromCharType*) source.getData() };
|
||||
|
||||
std::vector<juce_wchar> sourceChars;
|
||||
size_t requiredSize = 0;
|
||||
juce_wchar c;
|
||||
|
||||
while ((c = sourcePtr.getAndAdvance()) != '\0')
|
||||
{
|
||||
requiredSize += ToCharPointerType::getBytesRequiredFor (c);
|
||||
sourceChars.push_back (c);
|
||||
}
|
||||
|
||||
if (! removeNullTerminator)
|
||||
requiredSize += sizeof (ToCharType);
|
||||
|
||||
MemoryBlock result;
|
||||
result.setSize (requiredSize);
|
||||
|
||||
ToCharPointerType dest { (ToCharType*) result.getData() };
|
||||
|
||||
for (auto wc : sourceChars)
|
||||
dest.write (wc);
|
||||
|
||||
if (! removeNullTerminator)
|
||||
dest.writeNull();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct SeparatorStrings
|
||||
{
|
||||
std::vector<MemoryBlock> terminals, nulls;
|
||||
};
|
||||
|
||||
template <typename CharPointerType>
|
||||
SeparatorStrings getSeparators()
|
||||
{
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
template <>
|
||||
SeparatorStrings getSeparators<CharPointer_ASCII>()
|
||||
{
|
||||
SeparatorStrings result;
|
||||
|
||||
const CharPointer_ASCII::CharType* terminalCharPtrs[] = {
|
||||
"", "-", "+", "e", "e+", "E-", "f", " ", ",", ";", "<", "'", "\"", "_", "k",
|
||||
" +", " -", " -e", "-In ", " +n", "n", " r"
|
||||
};
|
||||
|
||||
for (auto ptr : terminalCharPtrs)
|
||||
result.terminals.push_back (memoryBlockFromCharPtr<CharPointer_ASCII> (ptr));
|
||||
|
||||
const CharPointer_ASCII::CharType* nullCharPtrs[] = { "." };
|
||||
|
||||
result.nulls = result.terminals;
|
||||
|
||||
for (auto ptr : nullCharPtrs)
|
||||
result.nulls.push_back (memoryBlockFromCharPtr<CharPointer_ASCII> (ptr));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <>
|
||||
SeparatorStrings getSeparators<CharPointer_UTF8>()
|
||||
{
|
||||
auto result = getSeparators<CharPointer_ASCII>();
|
||||
|
||||
const CharPointer_UTF8::CharType* terminalCharPtrs[] = {
|
||||
"\xe2\x82\xac", // €
|
||||
"\xf0\x90\x90\xB7", // 𐐷
|
||||
"\xf0\x9f\x98\x83", // 😃
|
||||
"\xf0\x9f\x8f\x81\xF0\x9F\x9A\x97" // 🏁🚗
|
||||
};
|
||||
|
||||
for (auto ptr : terminalCharPtrs)
|
||||
{
|
||||
auto block = memoryBlockFromCharPtr<CharPointer_UTF8> (ptr);
|
||||
|
||||
for (auto vec : { &result.terminals, &result.nulls })
|
||||
vec->push_back (block);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename CharPointerType, typename StorageType>
|
||||
SeparatorStrings prefixWithAsciiSeparators (const std::vector<std::vector<StorageType>>& terminalCharPtrs)
|
||||
{
|
||||
auto asciiSeparators = getSeparators<CharPointer_ASCII>();
|
||||
|
||||
SeparatorStrings result;
|
||||
|
||||
for (const auto& block : asciiSeparators.terminals)
|
||||
result.terminals.push_back (convert<CharPointer_ASCII, CharPointerType> (block));
|
||||
|
||||
for (const auto& block : asciiSeparators.nulls)
|
||||
result.nulls.push_back (convert<CharPointer_ASCII, CharPointerType> (block));
|
||||
|
||||
for (auto& t : terminalCharPtrs)
|
||||
{
|
||||
const auto block = memoryBlockFromCharPtr<CharPointerType> ((typename CharPointerType::CharType*) t.data());
|
||||
|
||||
for (auto vec : { &result.terminals, &result.nulls })
|
||||
vec->push_back (block);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <>
|
||||
SeparatorStrings getSeparators<CharPointer_UTF16>()
|
||||
{
|
||||
const std::vector<std::vector<char16_t>> terminalCharPtrs {
|
||||
{ 0x0 },
|
||||
{ 0x0076, 0x0 }, // v
|
||||
{ 0x20ac, 0x0 }, // €
|
||||
{ 0xd801, 0xdc37, 0x0 }, // 𐐷
|
||||
{ 0x0065, 0xd83d, 0xde03, 0x0 }, // e😃
|
||||
{ 0xd83c, 0xdfc1, 0xd83d, 0xde97, 0x0 } // 🏁🚗
|
||||
};
|
||||
|
||||
return prefixWithAsciiSeparators<CharPointer_UTF16> (terminalCharPtrs);
|
||||
}
|
||||
|
||||
template <>
|
||||
SeparatorStrings getSeparators<CharPointer_UTF32>()
|
||||
{
|
||||
const std::vector<std::vector<char32_t>> terminalCharPtrs = {
|
||||
{ 0x00000076, 0x0 }, // v
|
||||
{ 0x000020aC, 0x0 }, // €
|
||||
{ 0x00010437, 0x0 }, // 𐐷
|
||||
{ 0x00000065, 0x0001f603, 0x0 }, // e😃
|
||||
{ 0x0001f3c1, 0x0001f697, 0x0 } // 🏁🚗
|
||||
};
|
||||
|
||||
return prefixWithAsciiSeparators<CharPointer_UTF32> (terminalCharPtrs);
|
||||
}
|
||||
|
||||
template <typename TestFunction>
|
||||
void withAllPrefixesAndSuffixes (const std::vector<MemoryBlock>& prefixes,
|
||||
const std::vector<MemoryBlock>& suffixes,
|
||||
const std::vector<MemoryBlock>& testValues,
|
||||
TestFunction&& test)
|
||||
{
|
||||
for (const auto& prefix : prefixes)
|
||||
{
|
||||
for (const auto& testValue : testValues)
|
||||
{
|
||||
MemoryBlock testBlock = prefix;
|
||||
testBlock.append (testValue.getData(), testValue.getSize());
|
||||
|
||||
for (const auto& suffix : suffixes)
|
||||
{
|
||||
MemoryBlock data = testBlock;
|
||||
data.append (suffix.getData(), suffix.getSize());
|
||||
|
||||
test (data, suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CharPointerType>
|
||||
class CharacterFunctionsTests : public UnitTest
|
||||
{
|
||||
public:
|
||||
using CharType = typename CharPointerType::CharType;
|
||||
|
||||
CharacterFunctionsTests()
|
||||
: UnitTest ("CharacterFunctions", UnitTestCategories::text)
|
||||
{}
|
||||
|
|
@ -202,96 +392,159 @@ public:
|
|||
{
|
||||
beginTest ("readDoubleValue");
|
||||
|
||||
static const std::pair<String, double> testValues[] =
|
||||
const std::pair<const char*, double> trials[] =
|
||||
{
|
||||
// Integers
|
||||
STRING_DOUBLE_PAIR_COMBOS (0),
|
||||
STRING_DOUBLE_PAIR_COMBOS (3),
|
||||
STRING_DOUBLE_PAIR_COMBOS (4931),
|
||||
STRING_DOUBLE_PAIR_COMBOS (5000),
|
||||
STRING_DOUBLE_PAIR_COMBOS (9862097),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (3),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (4931),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (5000),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (9862097),
|
||||
|
||||
// Floating point numbers
|
||||
STRING_DOUBLE_PAIR_COMBOS (7.000),
|
||||
STRING_DOUBLE_PAIR_COMBOS (0.2),
|
||||
STRING_DOUBLE_PAIR_COMBOS (.298630),
|
||||
STRING_DOUBLE_PAIR_COMBOS (1.118),
|
||||
STRING_DOUBLE_PAIR_COMBOS (0.9000),
|
||||
STRING_DOUBLE_PAIR_COMBOS (0.0000001),
|
||||
STRING_DOUBLE_PAIR_COMBOS (500.0000001),
|
||||
STRING_DOUBLE_PAIR_COMBOS (9862098.2398604),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0.),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (9.),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (7.000),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0.2),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (.298630),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.118),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0.9000),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0.0000001),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (500.0000001),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (9862098.2398604),
|
||||
|
||||
// Exponents
|
||||
STRING_DOUBLE_PAIR_COMBOS (0e0),
|
||||
STRING_DOUBLE_PAIR_COMBOS (0.e0),
|
||||
STRING_DOUBLE_PAIR_COMBOS (0.00000e0),
|
||||
STRING_DOUBLE_PAIR_COMBOS (.0e7),
|
||||
STRING_DOUBLE_PAIR_COMBOS (0e-5),
|
||||
STRING_DOUBLE_PAIR_COMBOS (2E0),
|
||||
STRING_DOUBLE_PAIR_COMBOS (4.E0),
|
||||
STRING_DOUBLE_PAIR_COMBOS (1.2000000E0),
|
||||
STRING_DOUBLE_PAIR_COMBOS (1.2000000E6),
|
||||
STRING_DOUBLE_PAIR_COMBOS (.398e3),
|
||||
STRING_DOUBLE_PAIR_COMBOS (10e10),
|
||||
STRING_DOUBLE_PAIR_COMBOS (1.4962e+2),
|
||||
STRING_DOUBLE_PAIR_COMBOS (3198693.0973e4),
|
||||
STRING_DOUBLE_PAIR_COMBOS (10973097.2087E-4),
|
||||
STRING_DOUBLE_PAIR_COMBOS (1.3986e00006),
|
||||
STRING_DOUBLE_PAIR_COMBOS (2087.3087e+00006),
|
||||
STRING_DOUBLE_PAIR_COMBOS (6.0872e-00006),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0e0),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0.e0),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0.00000e0),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (.0e7),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (0e-5),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (2E0),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (4.E0),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.2000000E0),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.2000000E6),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (.398e3),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (10e10),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.4962e+2),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (3198693.0973e4),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (10973097.2087E-4),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.3986e00006),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (2087.3087e+00006),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (6.0872e-00006),
|
||||
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.7976931348623157e+308),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (2.2250738585072014e-308),
|
||||
|
||||
// Too many sig figs. The parsing routine on MinGW gets the last
|
||||
// significant figure wrong.
|
||||
STRING_DOUBLE_PAIR_COMBOS (17654321098765432.9),
|
||||
STRING_DOUBLE_PAIR_COMBOS (183456789012345678.9),
|
||||
STRING_DOUBLE_PAIR_COMBOS (1934567890123456789.9),
|
||||
STRING_DOUBLE_PAIR_COMBOS (20345678901234567891.9),
|
||||
STRING_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000000),
|
||||
STRING_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752e3),
|
||||
STRING_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752e100),
|
||||
STRING_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000000e-5),
|
||||
STRING_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000000e-40),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (17654321098765432.9),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (183456789012345678.9),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1934567890123456789.9),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (20345678901234567891.9),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000000),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752e3),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752e100),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000000e-5),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (10000000000000000303786028427003666890752.000005e-40),
|
||||
|
||||
STRING_DOUBLE_PAIR_COMBOS (1.23456789012345678901234567890),
|
||||
STRING_DOUBLE_PAIR_COMBOS (1.23456789012345678901234567890e-111)
|
||||
|
||||
// Limits. DBL_MAX may not exist on Linux.
|
||||
#if ! JUCE_LINUX
|
||||
, STRING_DOUBLE_PAIR (DBL_MAX),
|
||||
STRING_DOUBLE_PAIR (-DBL_MAX),
|
||||
STRING_DOUBLE_PAIR (DBL_MIN)
|
||||
#endif
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.23456789012345678901234567890),
|
||||
CHARPTR_DOUBLE_PAIR_COMBOS (1.23456789012345678901234567890e-111),
|
||||
};
|
||||
|
||||
for (auto trial : testValues)
|
||||
auto asciiToMemoryBlock = [] (const char* asciiPtr, bool removeNullTerminator)
|
||||
{
|
||||
auto charPtr = trial.first.getCharPointer();
|
||||
expectEquals (CharacterFunctions::readDoubleValue (charPtr), trial.second);
|
||||
}
|
||||
auto block = memoryBlockFromCharPtr<CharPointer_ASCII> (asciiPtr);
|
||||
return convert<CharPointer_ASCII, CharPointerType> (block, removeNullTerminator);
|
||||
};
|
||||
|
||||
const auto separators = getSeparators<CharPointerType>();
|
||||
|
||||
for (const auto& trial : trials)
|
||||
{
|
||||
String nans[] = { "NaN", "-nan", "+NAN", "1.0E1024", "-1.0E-999", "1.23456789012345678901234567890e123456789"};
|
||||
|
||||
for (auto nan : nans)
|
||||
for (const auto& terminal : separators.terminals)
|
||||
{
|
||||
auto charPtr = nan.getCharPointer();
|
||||
expect (std::isnan (CharacterFunctions::readDoubleValue (charPtr)));
|
||||
MemoryBlock data { asciiToMemoryBlock (trial.first, true) };
|
||||
data.append (terminal.getData(), terminal.getSize());
|
||||
|
||||
CharPointerType charPtr { (CharType*) data.getData() };
|
||||
expectEquals (CharacterFunctions::readDoubleValue (charPtr), trial.second);
|
||||
expect (*charPtr == *(CharPointerType ((CharType*) terminal.getData())));
|
||||
}
|
||||
}
|
||||
|
||||
auto asciiToMemoryBlocks = [&] (const std::vector<const char*>& asciiPtrs, bool removeNullTerminator)
|
||||
{
|
||||
String infs[] = { "Inf", "-inf", "INF"};
|
||||
std::vector<MemoryBlock> result;
|
||||
|
||||
for (auto inf : infs)
|
||||
for (auto* ptr : asciiPtrs)
|
||||
result.push_back (asciiToMemoryBlock (ptr, removeNullTerminator));
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
std::vector<const char*> prefixCharPtrs = { "" , "+", "-" };
|
||||
const auto prefixes = asciiToMemoryBlocks (prefixCharPtrs, true);
|
||||
|
||||
{
|
||||
std::vector<const char*> nanCharPtrs = { "NaN", "nan", "NAN", "naN" };
|
||||
auto nans = asciiToMemoryBlocks (nanCharPtrs, true);
|
||||
|
||||
withAllPrefixesAndSuffixes (prefixes, separators.terminals, nans, [this] (const MemoryBlock& data,
|
||||
const MemoryBlock& suffix)
|
||||
{
|
||||
auto charPtr = inf.getCharPointer();
|
||||
expect (std::isinf (CharacterFunctions::readDoubleValue (charPtr)));
|
||||
CharPointerType charPtr { (CharType*) data.getData() };
|
||||
expect (std::isnan (CharacterFunctions::readDoubleValue (charPtr)));
|
||||
expect (*charPtr == *(CharPointerType ((CharType*) suffix.getData())));
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<const char*> infCharPtrs = { "Inf", "inf", "INF", "InF", "1.0E1024", "1.23456789012345678901234567890e123456789" };
|
||||
auto infs = asciiToMemoryBlocks (infCharPtrs, true);
|
||||
|
||||
withAllPrefixesAndSuffixes (prefixes, separators.terminals, infs, [this] (const MemoryBlock& data,
|
||||
const MemoryBlock& suffix)
|
||||
{
|
||||
CharPointerType charPtr { (CharType*) data.getData() };
|
||||
auto expected = charPtr[0] == '-' ? -std::numeric_limits<double>::infinity()
|
||||
: std::numeric_limits<double>::infinity();
|
||||
expectEquals (CharacterFunctions::readDoubleValue (charPtr), expected);
|
||||
expect (*charPtr == *(CharPointerType ((CharType*) suffix.getData())));
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<const char*> zeroCharPtrs = { "1.0E-400", "1.23456789012345678901234567890e-123456789" };
|
||||
auto zeros = asciiToMemoryBlocks (zeroCharPtrs, true);
|
||||
|
||||
withAllPrefixesAndSuffixes (prefixes, separators.terminals, zeros, [this] (const MemoryBlock& data,
|
||||
const MemoryBlock& suffix)
|
||||
{
|
||||
CharPointerType charPtr { (CharType*) data.getData() };
|
||||
auto expected = charPtr[0] == '-' ? -0.0 : 0.0;
|
||||
expectEquals (CharacterFunctions::readDoubleValue (charPtr), expected);
|
||||
expect (*charPtr == *(CharPointerType ((CharType*) suffix.getData())));
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
for (const auto& n : separators.nulls)
|
||||
{
|
||||
MemoryBlock data { n.getData(), n.getSize() };
|
||||
CharPointerType charPtr { (CharType*) data.getData() };
|
||||
expectEquals (CharacterFunctions::readDoubleValue (charPtr), 0.0);
|
||||
expect (charPtr == CharPointerType { (CharType*) data.getData() }.findEndOfWhitespace());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static CharacterFunctionsTests characterFunctionsTests;
|
||||
static CharacterFunctionsTests<CharPointer_ASCII> characterFunctionsTestsAscii;
|
||||
static CharacterFunctionsTests<CharPointer_UTF8> characterFunctionsTestsUtf8;
|
||||
static CharacterFunctionsTests<CharPointer_UTF16> characterFunctionsTestsUtf16;
|
||||
static CharacterFunctionsTests<CharPointer_UTF32> characterFunctionsTestsUtf32;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -146,25 +146,27 @@ public:
|
|||
template <typename CharPointerType>
|
||||
static double readDoubleValue (CharPointerType& text) noexcept
|
||||
{
|
||||
#if JUCE_MINGW
|
||||
constexpr auto inf = std::numeric_limits<double>::infinity();
|
||||
|
||||
bool isNegative = false;
|
||||
#else
|
||||
#if ! JUCE_MINGW
|
||||
constexpr const int maxSignificantDigits = 17 + 1; // An additional digit for rounding
|
||||
constexpr const int bufferSize = maxSignificantDigits + 7 + 1; // -.E-XXX and a trailing null-terminator
|
||||
char buffer[(size_t) bufferSize] = {};
|
||||
char* currentCharacter = &(buffer[0]);
|
||||
char* writePtr = &(buffer[0]);
|
||||
#endif
|
||||
|
||||
text = text.findEndOfWhitespace();
|
||||
const auto endOfWhitspace = text.findEndOfWhitespace();
|
||||
text = endOfWhitspace;
|
||||
|
||||
auto c = *text;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '-':
|
||||
#if JUCE_MINGW
|
||||
isNegative = true;
|
||||
#else
|
||||
*currentCharacter++ = '-';
|
||||
#if ! JUCE_MINGW
|
||||
*writePtr++ = '-';
|
||||
#endif
|
||||
JUCE_FALLTHROUGH
|
||||
case '+':
|
||||
|
|
@ -178,15 +180,29 @@ public:
|
|||
{
|
||||
case 'n':
|
||||
case 'N':
|
||||
{
|
||||
if ((text[1] == 'a' || text[1] == 'A') && (text[2] == 'n' || text[2] == 'N'))
|
||||
{
|
||||
text += 3;
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
break;
|
||||
}
|
||||
|
||||
text = endOfWhitspace;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
case 'i':
|
||||
case 'I':
|
||||
{
|
||||
if ((text[1] == 'n' || text[1] == 'N') && (text[2] == 'f' || text[2] == 'F'))
|
||||
return std::numeric_limits<double>::infinity();
|
||||
break;
|
||||
{
|
||||
text += 3;
|
||||
return isNegative ? -inf : inf;
|
||||
}
|
||||
|
||||
text = endOfWhitspace;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
|
|
@ -299,9 +315,8 @@ public:
|
|||
|
||||
#else // ! JUCE_MINGW
|
||||
|
||||
int numSigFigs = 0;
|
||||
bool decimalPointFound = false;
|
||||
int extraExponent = 0;
|
||||
int numSigFigs = 0, extraExponent = 0;
|
||||
bool decimalPointFound = false, leadingZeros = false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
|
|
@ -323,16 +338,19 @@ public:
|
|||
}
|
||||
|
||||
if (numSigFigs == 0 && digit == 0)
|
||||
{
|
||||
leadingZeros = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
*currentCharacter++ = (char) ('0' + (char) digit);
|
||||
*writePtr++ = (char) ('0' + (char) digit);
|
||||
numSigFigs++;
|
||||
}
|
||||
else if ((! decimalPointFound) && *text == '.')
|
||||
{
|
||||
++text;
|
||||
*currentCharacter++ = '.';
|
||||
*writePtr++ = '.';
|
||||
decimalPointFound = true;
|
||||
}
|
||||
else
|
||||
|
|
@ -341,7 +359,11 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
c = *text;
|
||||
if ((! leadingZeros) && (numSigFigs == 0))
|
||||
{
|
||||
text = endOfWhitspace;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
auto writeExponentDigits = [] (int exponent, char* destination)
|
||||
{
|
||||
|
|
@ -358,19 +380,28 @@ public:
|
|||
*destination++ = (char) ('0' + (char) exponent);
|
||||
};
|
||||
|
||||
if ((c == 'e' || c == 'E') && numSigFigs > 0)
|
||||
c = *text;
|
||||
|
||||
if (c == 'e' || c == 'E')
|
||||
{
|
||||
*currentCharacter++ = 'e';
|
||||
const auto startOfExponent = text;
|
||||
*writePtr++ = 'e';
|
||||
bool parsedExponentIsPositive = true;
|
||||
|
||||
switch (*++text)
|
||||
{
|
||||
case '-': parsedExponentIsPositive = false; JUCE_FALLTHROUGH
|
||||
case '+': ++text; break;
|
||||
default: break;
|
||||
case '-':
|
||||
parsedExponentIsPositive = false;
|
||||
JUCE_FALLTHROUGH
|
||||
case '+':
|
||||
++text;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
int exponent = 0;
|
||||
const auto startOfExponentDigits = text;
|
||||
|
||||
while (text.isDigit())
|
||||
{
|
||||
|
|
@ -380,22 +411,30 @@ public:
|
|||
exponent = (exponent * 10) + digit;
|
||||
}
|
||||
|
||||
if (text == startOfExponentDigits)
|
||||
text = startOfExponent;
|
||||
|
||||
exponent = extraExponent + (parsedExponentIsPositive ? exponent : -exponent);
|
||||
|
||||
if (exponent < 0)
|
||||
*currentCharacter++ = '-';
|
||||
{
|
||||
if (exponent < std::numeric_limits<double>::min_exponent10 - 1)
|
||||
return isNegative ? -0.0 : 0.0;
|
||||
|
||||
exponent = std::abs (exponent);
|
||||
*writePtr++ = '-';
|
||||
exponent = -exponent;
|
||||
}
|
||||
else if (exponent > std::numeric_limits<double>::max_exponent10 + 1)
|
||||
{
|
||||
return isNegative ? -inf : inf;
|
||||
}
|
||||
|
||||
if (exponent > std::numeric_limits<double>::max_exponent10)
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
writeExponentDigits (exponent, currentCharacter);
|
||||
writeExponentDigits (exponent, writePtr);
|
||||
}
|
||||
else if (extraExponent > 0)
|
||||
{
|
||||
*currentCharacter++ = 'e';
|
||||
writeExponentDigits (extraExponent, currentCharacter);
|
||||
*writePtr++ = 'e';
|
||||
writeExponentDigits (extraExponent, writePtr);
|
||||
}
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue