SkFloatToDecimal: optimize the less common cases.

bench PDFScalar_random goes from 120 ns to 70 ns.

Change-Id: I6254f5c900395ee470ffee26303915025a8f0dda
Reviewed-on: https://skia-review.googlesource.com/131151
Commit-Queue: Ben Wagner <bungeman@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
Auto-Submit: Hal Canary <halcanary@google.com>
diff --git a/bench/PDFBench.cpp b/bench/PDFBench.cpp
index bebf2b9..4cda80b 100644
--- a/bench/PDFBench.cpp
+++ b/bench/PDFBench.cpp
@@ -40,21 +40,35 @@
 // Test speed of SkFloatToDecimal for typical floats that
 // might be found in a PDF document.
 struct PDFScalarBench : public Benchmark {
+    PDFScalarBench(const char* n, float (*f)(SkRandom*)) : fName(n), fNextFloat(f) {}
+    const char* fName;
+    float (*fNextFloat)(SkRandom*);
     bool isSuitableFor(Backend b) override {
         return b == kNonRendering_Backend;
     }
-    const char* onGetName() override { return "PDFScalar"; }
+    const char* onGetName() override { return fName; }
     void onDraw(int loops, SkCanvas*) override {
         SkRandom random;
         char dst[kMaximumSkFloatToDecimalLength];
         while (loops-- > 0) {
-            auto f = random.nextRangeF(-500.0f, 1500.0f);
+            auto f = fNextFloat(&random);
             (void)SkFloatToDecimal(f, dst);
         }
     }
 };
 
-DEF_BENCH(return new PDFScalarBench;)
+float next_common(SkRandom* random) {
+    return random->nextRangeF(-500.0f, 1500.0f);
+}
+float next_any(SkRandom* random) {
+    union { uint32_t u; float f; };
+    u = random->nextU();
+    static_assert(sizeof(float) == sizeof(uint32_t), "");
+    return f;
+}
+
+DEF_BENCH(return new PDFScalarBench("PDFScalar_common", next_common);)
+DEF_BENCH(return new PDFScalarBench("PDFScalar_random", next_any);)
 
 #ifdef SK_SUPPORT_PDF
 
diff --git a/src/utils/SkFloatToDecimal.cpp b/src/utils/SkFloatToDecimal.cpp
index 273175a..f2a1553 100644
--- a/src/utils/SkFloatToDecimal.cpp
+++ b/src/utils/SkFloatToDecimal.cpp
@@ -13,6 +13,22 @@
 
 #include "SkTypes.h"
 
+// returns `value * pow(base, e)`, assuming `e` is positive.
+static double pow_by_squaring(double value, double base, int e) {
+    // https://en.wikipedia.org/wiki/Exponentiation_by_squaring
+    SkASSERT(e > 0);
+    while (true) {
+        if (e & 1) {
+            value *= base;
+        }
+        e >>= 1;
+        if (0 == e) {
+            return value;
+        }
+        base *= base;
+    }
+}
+
 // Return pow(10.0, e), optimized for common cases.
 static double pow10(int e) {
     switch (e) {
@@ -34,14 +50,10 @@
         case 15: return 1e+15;
         default:
             if (e > 15) {
-                double value = 1e+15;
-                while (e-- > 15) { value *= 10.0; }
-                return value;
+                return pow_by_squaring(1e+15, 10.0, e - 15);
             } else {
                 SkASSERT(e < 0);
-                double value = 1.0;
-                while (e++ < 0) { value /= 10.0; }
-                return value;
+                return pow_by_squaring(1.0, 0.1, -e);
             }
     }
 }