Chromium Code Reviews| Index: src/pdf/SkPDFUtils.cpp |
| diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp |
| index b8d65092b07e8065f8cce15e2ba60fbf3eff7cc3..3a4033e9a956a231fea1ca3e7e5da0c875b94bf6 100644 |
| --- a/src/pdf/SkPDFUtils.cpp |
| +++ b/src/pdf/SkPDFUtils.cpp |
| @@ -258,6 +258,39 @@ void SkPDFUtils::AppendScalar(SkScalar value, SkWStream* stream) { |
| stream->write(result, len); |
| } |
| +// Return pow(10.0, e), optimized for common cases. |
|
tomhudson
2016/07/15 20:45:41
Huh, this is really faster?
hal.canary
2016/07/17 02:33:42
yep.
|
| +inline double pow10(int e) { |
| + switch (e) { |
| + case 0: return 1.0; // common cases |
| + case 1: return 10.0; |
| + case 2: return 100.0; |
| + case 3: return 1e+03; |
| + case 4: return 1e+04; |
| + case 5: return 1e+05; |
| + case 6: return 1e+06; |
| + case 7: return 1e+07; |
| + case 8: return 1e+08; |
| + case 9: return 1e+09; |
| + case 10: return 1e+10; |
| + case 11: return 1e+11; |
| + case 12: return 1e+12; |
| + case 13: return 1e+13; |
| + case 14: return 1e+14; |
| + case 15: return 1e+15; |
| + default: |
| + if (e > 15) { |
| + double value = 1e+15; |
| + while (e-- > 15) { value *= 10.0; } |
| + return value; |
| + } else { |
| + SkASSERT(e < 0); |
| + double value = 1.0; |
| + while (e++ < 0) { value /= 10.0; } |
| + return value; |
| + } |
| + } |
| +} |
| + |
| /** Write a string into result, includeing a terminating '\0' (for |
| unit testing). Return strlen(result) (for SkWStream::write) The |
| resulting string will be in the form /[-]?([0-9]*.)?[0-9]+/ and |
| @@ -303,65 +336,72 @@ size_t SkPDFUtils::FloatToDecimal(float value, |
| *output = '\0'; |
| return output - result; |
| } |
| - // Inspired by: |
| - // http://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal-conversion/ |
| - |
| if (value < 0.0) { |
| *output++ = '-'; |
| value = -value; |
| } |
| SkASSERT(value >= 0.0f); |
| - // Must use double math to keep precision right. |
| - double intPart; |
| - double fracPart = std::modf(static_cast<double>(value), &intPart); |
| - SkASSERT(intPart + fracPart == static_cast<double>(value)); |
| - size_t significantDigits = 0; |
| - const size_t maxSignificantDigits = 9; |
| - // Any fewer significant digits loses precision. The unit test |
| - // checks round-trip correctness. |
| - SkASSERT(intPart >= 0.0 && fracPart >= 0.0); // negative handled already. |
| - SkASSERT(intPart > 0.0 || fracPart > 0.0); // zero already caught. |
| - if (intPart > 0.0) { |
| - // put the intPart digits onto a stack for later reversal. |
| - char reversed[1 + FLT_MAX_10_EXP]; // 39 == 1 + FLT_MAX_10_EXP |
| - // the largest integer part is FLT_MAX; it has 39 decimal digits. |
| - size_t reversedIndex = 0; |
| - do { |
| - SkASSERT(reversedIndex < sizeof(reversed)); |
| - int digit = static_cast<int>(std::fmod(intPart, 10.0)); |
| - SkASSERT(digit >= 0 && digit <= 9); |
| - reversed[reversedIndex++] = '0' + digit; |
| - intPart = std::floor(intPart / 10.0); |
| - } while (intPart > 0.0); |
| - significantDigits = reversedIndex; |
| - SkASSERT(reversedIndex <= sizeof(reversed)); |
| - SkASSERT(output + reversedIndex <= end); |
| - while (reversedIndex-- > 0) { // pop from stack, append to result |
| - *output++ = reversed[reversedIndex]; |
| - } |
| + int binaryExponents; |
|
tomhudson
2016/07/15 20:45:41
Bikeshed: Why is this name plural?
hal.canary
2016/07/17 02:33:42
Done.
|
| + (void)frexp(value, &binaryExponents); |
|
tomhudson
2016/07/15 20:45:41
std::frexp()? Or is that not our style?
hal.canary
2016/07/17 02:33:42
We do it both ways. the C++ish way seems to be ga
|
| + static const double kLog2 = 0.3010299956639812; // log10(2.0); |
|
tomhudson
2016/07/15 20:45:41
constexpr?
hal.canary
2016/07/17 02:33:42
std::log10 isn't constexpr.
|
| + int decimalExponents = (int)floor(kLog2 * binaryExponents); |
|
tomhudson
2016/07/15 20:45:41
Bikeshed as above
hal.canary
2016/07/17 02:33:42
Done.
|
| + int decShift = decimalExponents - 8; |
| + double power = pow10(-decShift); |
|
tomhudson
2016/07/15 20:45:41
(OK, convinced myself that in the common case pow1
hal.canary
2016/07/17 02:33:42
Done.
|
| + int32_t d = (int32_t)(value * power + 0.5); |
| + //SkASSERT(value == (float)(d * pow(10.0, decShift))); |
| + SkASSERT(d <= 999999999); |
| + if (d > 167772159) { // floor(pow(10,1+log10(1<<24))) |
| + // need one fewer decimal digits for 24-bit precision. |
| + decShift = decimalExponents - 7; |
| + //SkASSERT(power * 0.1 = pow10(-decShift)); |
| + d = (int32_t)(value * (power * 0.1) + 0.5); |
| + SkASSERT(d <= 99999999); |
| } |
| - if (fracPart > 0 && significantDigits < maxSignificantDigits) { |
| - *output++ = '.'; |
| - SkASSERT(output <= end); |
| + while (d % 10 == 0) { |
| + d /= 10; |
| + ++decShift; |
| + } |
| + SkASSERT(d > 0); |
| + //SkASSERT(value == (float)(d * pow(10.0, decShift))); |
| + uint8_t buffer[9]; // decimal value buffer. |
| + int bufferIndex = 0; |
| + do { |
| + buffer[bufferIndex++] = d % 10; |
| + d /= 10; |
| + } while (d != 0); |
| + SkASSERT(bufferIndex <= (int)sizeof(buffer) && bufferIndex > 0); |
| + if (decShift >= 0) { |
| do { |
| - fracPart = std::modf(fracPart * 10.0, &intPart); |
| - int digit = static_cast<int>(intPart); |
| - SkASSERT(digit >= 0 && digit <= 9); |
| - *output++ = '0' + digit; |
| - SkASSERT(output <= end); |
| - if (digit > 0 || significantDigits > 0) { |
| - // start counting significantDigits after first non-zero digit. |
| - ++significantDigits; |
| + --bufferIndex; |
| + *output++ = '0' + buffer[bufferIndex]; |
| + } while (bufferIndex); |
| + for (int i = 0; i < decShift; ++i) { |
| + *output++ = '0'; |
| + } |
| + } else { |
| + int N = bufferIndex + decShift; |
| + if (N > 0) { |
| + for (int i = 0; i < N; ++i) { |
| + --bufferIndex; |
| + *output++ = '0' + buffer[bufferIndex]; |
| + } |
| + *output++ = '.'; |
| + } else { |
| + *output++ = '.'; |
| + for (int i = 0; i < -N; ++i) { |
| + *output++ = '0'; |
| } |
| - } while (fracPart > 0.0 |
| - && significantDigits < maxSignificantDigits |
| - && output < end); |
| - // When fracPart == 0, additional digits will be zero. |
| - // When significantDigits == maxSignificantDigits, we can stop. |
| - // when output == end, we have filed the string. |
| - // Note: denormalized numbers will not have the same number of |
| - // significantDigits, but do not need them to round-trip. |
| + } |
| + while (bufferIndex > 0) { |
| + --bufferIndex; |
| + *output++ = '0' + buffer[bufferIndex]; |
| + if (output == end) { |
| + break; // denormalized: don't need extra precision. |
| + // Note: denormalized numbers will not have the same number of |
| + // significantDigits, but do not need them to round-trip. |
| + } |
| + } |
| } |
| SkASSERT(output <= end); |
| *output = '\0'; |