Permalink
Browse files

Fixed some issues when parsing double strings

  • Loading branch information...
tpoole committed Jul 20, 2017
1 parent 6bfcd82 commit 7e6a650e8c35e2459abb73949b1198ee5aa15870
Showing with 139 additions and 72 deletions.
  1. +104 −0 modules/juce_core/text/juce_CharacterFunctions.cpp
  2. +35 −72 modules/juce_core/text/juce_CharacterFunctions.h
@@ -169,3 +169,107 @@ juce_wchar CharacterFunctions::getUnicodeCharFromWindows1252Codepage (const uint

return (juce_wchar) lookup[c - 0x80];
}

//==============================================================================
#if JUCE_UNIT_TESTS

#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)

class CharacterFunctionsTests : public UnitTest
{
public:
CharacterFunctionsTests() : UnitTest ("CharacterFunctions", "Text") {}

void runTest() override
{
beginTest ("readDoubleValue");

static const std::pair<String, double> testValues[] =
{
// 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),

// 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),

// 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),

// Too many sig figs
STRING_DOUBLE_PAIR_COMBOS (1.23456789012345678901234567890),
STRING_DOUBLE_PAIR_COMBOS (1.23456789012345678901234567890e-111)

#if ! JUCE_LINUX
// Limits
, STRING_DOUBLE_PAIR (DBL_MAX),
STRING_DOUBLE_PAIR (-DBL_MAX),
STRING_DOUBLE_PAIR (DBL_MIN)
#endif
};

for (auto trial : testValues)
{
auto charPtr = trial.first.getCharPointer();
expectEquals (CharacterFunctions::readDoubleValue (charPtr), trial.second);
}

{
String nans[] = { "NaN", "-nan", "+NAN", "1.0E1024", "-1.0E-999", "1.23456789012345678901234567890e123456789"};
for (auto nan : nans)
{
auto charPtr = nan.getCharPointer();
expect (std::isnan (CharacterFunctions::readDoubleValue (charPtr)));
}
}

{
String infs[] = { "Inf", "-inf", "INF"};
for (auto inf : infs)
{
auto charPtr = inf.getCharPointer();
expect (std::isinf (CharacterFunctions::readDoubleValue (charPtr)));
}
}
}
};

static CharacterFunctionsTests characterFunctionsTests;

#endif
@@ -127,19 +127,19 @@ class JUCE_API CharacterFunctions
template <typename CharPointerType>
static double readDoubleValue (CharPointerType& text) noexcept
{
double result[3] = { 0 }, accumulator[2] = { 0 };
int exponentAdjustment[2] = { 0 }, exponentAccumulator[2] = { -1, -1 };
int exponent = 0, decPointIndex = 0, digit = 0;
int lastDigit = 0, numSignificantDigits = 0;
bool isNegative = false, digitsFound = false;
const int maxSignificantDigits = 15 + 2;
const int maxSignificantDigits = 17 + 1; // An additional digit for rounding
const int bufferSize = maxSignificantDigits + 7 + 1; // -.E-XXX and a trailing null-terminator
char buffer[bufferSize] = {};
char* currentCharacter = &(buffer[0]);
int numSigFigs = 0;
bool decimalPointFound = false;

text = text.findEndOfWhitespace();
juce_wchar c = *text;

switch (c)
{
case '-': isNegative = true; // fall-through..
case '-': *currentCharacter++ = '-'; // Fall-through..
case '+': c = *++text;
}

@@ -162,96 +162,59 @@ class JUCE_API CharacterFunctions
{
if (text.isDigit())
{
lastDigit = digit;
digit = (int) text.getAndAdvance() - '0';
digitsFound = true;
int digit = (int) text.getAndAdvance() - '0';

if (decPointIndex != 0)
exponentAdjustment[1]++;

if (numSignificantDigits == 0 && digit == 0)
if (numSigFigs >= maxSignificantDigits
|| ((numSigFigs == 0 && (! decimalPointFound)) && digit == 0))
continue;

if (++numSignificantDigits > maxSignificantDigits)
{
if (digit > 5)
++accumulator [decPointIndex];
else if (digit == 5 && (lastDigit & 1) != 0)
++accumulator [decPointIndex];

if (decPointIndex > 0)
exponentAdjustment[1]--;
else
exponentAdjustment[0]++;

while (text.isDigit())
{
++text;
if (decPointIndex == 0)
exponentAdjustment[0]++;
}
}
else
{
const double maxAccumulatorValue = (double) ((std::numeric_limits<unsigned int>::max() - 9) / 10);
if (accumulator [decPointIndex] > maxAccumulatorValue)
{
result [decPointIndex] = mulexp10 (result [decPointIndex], exponentAccumulator [decPointIndex])
+ accumulator [decPointIndex];
accumulator [decPointIndex] = 0;
exponentAccumulator [decPointIndex] = 0;
}

accumulator [decPointIndex] = accumulator[decPointIndex] * 10 + digit;
exponentAccumulator [decPointIndex]++;
}
*currentCharacter++ = '0' + (char) digit;
numSigFigs++;
}
else if (decPointIndex == 0 && *text == '.')
else if ((! decimalPointFound) && *text == '.')
{
++text;
decPointIndex = 1;

if (numSignificantDigits > maxSignificantDigits)
{
while (text.isDigit())
++text;
break;
}
*currentCharacter++ = '.';
decimalPointFound = true;
}
else
{
break;
}
}

result[0] = mulexp10 (result[0], exponentAccumulator[0]) + accumulator[0];

if (decPointIndex != 0)
result[1] = mulexp10 (result[1], exponentAccumulator[1]) + accumulator[1];

c = *text;
if ((c == 'e' || c == 'E') && digitsFound)
if ((c == 'e' || c == 'E') && numSigFigs > 0)
{
bool negativeExponent = false;
*currentCharacter++ = 'e';

switch (*++text)
{
case '-': negativeExponent = true; // fall-through..
case '-': *currentCharacter++ = '-'; // fall-through..
case '+': ++text;
}

int exponentMagnitude = 0;

while (text.isDigit())
exponent = (exponent * 10) + ((int) text.getAndAdvance() - '0');
{
if (currentCharacter == std::end (buffer) - 1)

This comment has been minimized.

@JosephTLyons

JosephTLyons Jul 22, 2017

Line 201 is returning an issue:

"No member named 'end' found in namespace std"

This comment has been minimized.

@tpoole

tpoole Jul 22, 2017

Author Collaborator

I've just pushed a fix for this.

return std::numeric_limits<double>::quiet_NaN();

if (negativeExponent)
exponent = -exponent;
}
int digit = (int) text.getAndAdvance() - '0';

double r = mulexp10 (result[0], exponent + exponentAdjustment[0]);
if (decPointIndex != 0)
r += mulexp10 (result[1], exponent - exponentAdjustment[1]);
if (digit != 0 || exponentMagnitude != 0)
{
*currentCharacter++ = '0' + (char) digit;
exponentMagnitude = (exponentMagnitude * 10) + digit;
}
}

if (exponentMagnitude > std::numeric_limits<double>::max_exponent10)
return std::numeric_limits<double>::quiet_NaN();
}

return isNegative ? -r : r;
return strtod (&buffer[0], nullptr);
}

/** Parses a character string, to read a floating-point value. */

0 comments on commit 7e6a650

Please sign in to comment.