SkPDF: re-work SkPDFUtils::FloatToDecimal

  * do a lot less floating-point math by converting to
    an integer as early as possible [faster].
  * round rather than truncate.
  * use 8 significant digits rather than 9 when possible.
  * remove trailing zeros in fractions.

before:
      0.12 !    PDFScalar   nonrendering
after:
      0.07 !    PDFScalar   nonrendering

Accuracy guaranteed by existing unit test.

Example diffs:

    -/Shading <</Function <</C0 [.321568638 .333333343 .321568638]
    +/Shading <</Function <</C0 [.32156864 .33333334 .32156864]

    -/C1 [.258823543 .270588248 .258823543]
    +/C1 [.25882354 .27058825 .25882354]

    -1 0 0 -1 20 120.394500 Tm
    +1 0 0 -1 20 120.394501 Tm

    -1 0 0 -1 20 184.789001 Tm
    +1 0 0 -1 20 184.789 Tm

    -291.503997 0 l
    +291.504 0 l

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2146103004

Review-Url: https://codereview.chromium.org/2146103004
diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp
index 9e4ac51..0fe6fb5 100644
--- a/src/pdf/SkPDFUtils.cpp
+++ b/src/pdf/SkPDFUtils.cpp
@@ -282,6 +282,39 @@
     stream->write(result, len);
 }
 
+// Return pow(10.0, e), optimized for common cases.
+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
@@ -327,65 +360,74 @@
         *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 binaryExponent;
+    (void)std::frexp(value, &binaryExponent);
+    static const double kLog2 = 0.3010299956639812;  // log10(2.0);
+    int decimalExponent = static_cast<int>(std::floor(kLog2 * binaryExponent));
+    int decimalShift = decimalExponent - 8;
+    double power = pow10(-decimalShift);
+    int32_t d = static_cast<int32_t>(value * power + 0.5);
+    // SkASSERT(value == (float)(d * pow(10.0, decimalShift)));
+    SkASSERT(d <= 999999999);
+    if (d > 167772159) {  // floor(pow(10,1+log10(1<<24)))
+       // need one fewer decimal digits for 24-bit precision.
+       decimalShift = decimalExponent - 7;
+       // SkASSERT(power * 0.1 = pow10(-decimalShift));
+       // recalculate to get rounding right.
+       d = static_cast<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;
+        ++decimalShift;
+    }
+    SkASSERT(d > 0);
+    // SkASSERT(value == (float)(d * pow(10.0, decimalShift)));
+    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 (decimalShift >= 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 < decimalShift; ++i) {
+            *output++ = '0';
+        }
+    } else {
+        int placesBeforeDecimal = bufferIndex + decimalShift;
+        if (placesBeforeDecimal > 0) {
+            while (placesBeforeDecimal-- > 0) {
+                --bufferIndex;
+                *output++ = '0' + buffer[bufferIndex];
             }
-        } 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.
+            *output++ = '.';
+        } else {
+            *output++ = '.';
+            int placesAfterDecimal = -placesBeforeDecimal;
+            while (placesAfterDecimal-- > 0) {
+                *output++ = '0';
+            }
+        }
+        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';
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index d816b60..cef1150 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -27,6 +27,9 @@
 #include "Test.h"
 #include "sk_tool_utils.h"
 
+#include <cstdlib>
+#include <cmath>
+
 #define DUMMY_TEXT "DCT compessed stream."
 
 namespace {
@@ -44,9 +47,8 @@
         catPtr = &catalog;
     }
     obj.emitObject(&buffer, catPtr->numbers, catPtr->substitutes);
-    SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
-    SkString tmp(asset->getLength());
-    asset->read(tmp.writable_str(), asset->getLength());
+    SkString tmp(buffer.bytesWritten());
+    buffer.copyTo(tmp.writable_str());
     return tmp;
 }
 
@@ -54,42 +56,41 @@
     return len == str.size() && 0 == memcmp(str.c_str(), strPtr, len);
 }
 
-#define ASSERT_EQL(REPORTER, SKSTRING, STRING, LEN)                     \
-    do {                                                                \
-        const char* strptr = STRING;                                    \
-        const SkString& sks = SKSTRING;                                 \
-        if (!eq(sks, strptr, LEN)) {                                    \
-            REPORT_FAILURE(                                             \
-                    REPORTER,                                           \
-                    "",                                                 \
-                    SkStringPrintf("'%s' != '%s'", strptr, sks.c_str()));  \
-        }                                                               \
-    } while (false)
+static void assert_eql(skiatest::Reporter* reporter,
+                       const SkString& skString,
+                       const char* str,
+                       size_t len) {
+    if (!eq(skString, str, len)) {
+        REPORT_FAILURE(reporter, "", SkStringPrintf(
+                "'%*s' != '%s'", len, str, skString.c_str()));
+    }
+}
 
-#define ASSERT_EQ(REPORTER, SKSTRING, STRING)             \
-    do {                                                  \
-        const char* str = STRING;                         \
-        ASSERT_EQL(REPORTER, SKSTRING, str, strlen(str)); \
-    } while (false)
-
-#define ASSERT_EMIT_EQ(REPORTER, OBJECT, STRING)          \
-    do {                                                  \
-        SkString result = emit_to_string(OBJECT);         \
-        ASSERT_EQ(REPORTER, result, STRING);              \
-    } while (false)
+static void assert_eq(skiatest::Reporter* reporter,
+                      const SkString& skString,
+                      const char* str) {
+    assert_eql(reporter, skString, str, strlen(str));
+}
 
 
+template <typename T>
+static void assert_emit_eq(skiatest::Reporter* reporter,
+                           T& object,
+                           const char* string) {
+    SkString result = emit_to_string(object);
+    assert_eq(reporter, result, string);
+}
 
 static void TestPDFStream(skiatest::Reporter* reporter) {
     char streamBytes[] = "Test\nFoo\tBar";
     SkAutoTDelete<SkMemoryStream> streamData(new SkMemoryStream(
         streamBytes, strlen(streamBytes), true));
     sk_sp<SkPDFStream> stream(new SkPDFStream(streamData.get()));
-    ASSERT_EMIT_EQ(reporter,
+    assert_emit_eq(reporter,
                    *stream,
                    "<</Length 12>> stream\nTest\nFoo\tBar\nendstream");
     stream->insertInt("Attribute", 42);
-    ASSERT_EMIT_EQ(reporter,
+    assert_emit_eq(reporter,
                    *stream,
                    "<</Length 12\n/Attribute 42>> stream\n"
                    "Test\nFoo\tBar\nendstream");
@@ -115,7 +116,7 @@
         expected.writeText("\nendstream");
         SkAutoDataUnref expectedResultData2(expected.copyToData());
         SkString result = emit_to_string(*stream);
-        ASSERT_EQL(reporter,
+        assert_eql(reporter,
                    result,
                    (const char*)expectedResultData2->data(),
                    expectedResultData2->size());
@@ -154,7 +155,7 @@
     SkString result = emit_to_string(*a2, &catalog);
     // If appendObjRef misbehaves, then the result would
     // be [[]], not [1 0 R].
-    ASSERT_EQ(reporter, result, "[1 0 R]");
+    assert_eq(reporter, result, "[1 0 R]");
 }
 
 static void TestSubstitute(skiatest::Reporter* reporter) {
@@ -190,91 +191,94 @@
     doc->close();
 }
 
+static void assert_emit_eq_number(skiatest::Reporter* reporter, float number) {
+    SkPDFUnion pdfUnion = SkPDFUnion::Scalar(number);
+    SkString result = emit_to_string(pdfUnion);
+    float value = static_cast<float>(std::atof(result.c_str()));
+    if (value != number) {
+        ERRORF(reporter, "%.9g != %s", number, result.c_str());
+    }
+}
+
+
 static void TestPDFUnion(skiatest::Reporter* reporter) {
     SkPDFUnion boolTrue = SkPDFUnion::Bool(true);
-    ASSERT_EMIT_EQ(reporter, boolTrue, "true");
+    assert_emit_eq(reporter, boolTrue, "true");
 
     SkPDFUnion boolFalse = SkPDFUnion::Bool(false);
-    ASSERT_EMIT_EQ(reporter, boolFalse, "false");
+    assert_emit_eq(reporter, boolFalse, "false");
 
     SkPDFUnion int42 = SkPDFUnion::Int(42);
-    ASSERT_EMIT_EQ(reporter, int42, "42");
+    assert_emit_eq(reporter, int42, "42");
 
-    SkPDFUnion realHalf = SkPDFUnion::Scalar(SK_ScalarHalf);
-    ASSERT_EMIT_EQ(reporter, realHalf, ".5");
-
-    SkPDFUnion bigScalar = SkPDFUnion::Scalar(110999.75f);
-    ASSERT_EMIT_EQ(reporter, bigScalar, "110999.75");
-
-    SkPDFUnion biggerScalar = SkPDFUnion::Scalar(50000000.1f);
-    ASSERT_EMIT_EQ(reporter, biggerScalar, "50000000");
-
-    SkPDFUnion smallestScalar = SkPDFUnion::Scalar(1.0f / 65536);
-    ASSERT_EMIT_EQ(reporter, smallestScalar, ".0000152587890");
+    assert_emit_eq_number(reporter, SK_ScalarHalf);
+    assert_emit_eq_number(reporter, 110999.75f);  // bigScalar
+    assert_emit_eq_number(reporter, 50000000.1f);  // biggerScalar
+    assert_emit_eq_number(reporter, 1.0f / 65536);  // smallScalar
 
     SkPDFUnion stringSimple = SkPDFUnion::String("test ) string ( foo");
-    ASSERT_EMIT_EQ(reporter, stringSimple, "(test \\) string \\( foo)");
+    assert_emit_eq(reporter, stringSimple, "(test \\) string \\( foo)");
 
     SkString stringComplexInput("\ttest ) string ( foo");
     SkPDFUnion stringComplex = SkPDFUnion::String(stringComplexInput);
-    ASSERT_EMIT_EQ(reporter, stringComplex, "(\\011test \\) string \\( foo)");
+    assert_emit_eq(reporter, stringComplex, "(\\011test \\) string \\( foo)");
 
     SkString binaryStringInput("\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20");
     SkPDFUnion binaryString = SkPDFUnion::String(binaryStringInput);
-    ASSERT_EMIT_EQ(reporter, binaryString, "<0102030405060708090A0B0C0D0E0F10>");
+    assert_emit_eq(reporter, binaryString, "<0102030405060708090A0B0C0D0E0F10>");
 
     SkString nameInput("Test name\twith#tab");
     SkPDFUnion name = SkPDFUnion::Name(nameInput);
-    ASSERT_EMIT_EQ(reporter, name, "/Test#20name#09with#23tab");
+    assert_emit_eq(reporter, name, "/Test#20name#09with#23tab");
 
     SkString nameInput2("A#/%()<>[]{}B");
     SkPDFUnion name2 = SkPDFUnion::Name(nameInput2);
-    ASSERT_EMIT_EQ(reporter, name2, "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB");
+    assert_emit_eq(reporter, name2, "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB");
 
     SkPDFUnion name3 = SkPDFUnion::Name("SimpleNameWithOnlyPrintableASCII");
-    ASSERT_EMIT_EQ(reporter, name3, "/SimpleNameWithOnlyPrintableASCII");
+    assert_emit_eq(reporter, name3, "/SimpleNameWithOnlyPrintableASCII");
 
     // Test that we correctly handle characters with the high-bit set.
     SkString highBitString("\xDE\xAD" "be\xEF");
     SkPDFUnion highBitName = SkPDFUnion::Name(highBitString);
-    ASSERT_EMIT_EQ(reporter, highBitName, "/#DE#ADbe#EF");
+    assert_emit_eq(reporter, highBitName, "/#DE#ADbe#EF");
 }
 
 static void TestPDFArray(skiatest::Reporter* reporter) {
     sk_sp<SkPDFArray> array(new SkPDFArray);
-    ASSERT_EMIT_EQ(reporter, *array, "[]");
+    assert_emit_eq(reporter, *array, "[]");
 
     array->appendInt(42);
-    ASSERT_EMIT_EQ(reporter, *array, "[42]");
+    assert_emit_eq(reporter, *array, "[42]");
 
     array->appendScalar(SK_ScalarHalf);
-    ASSERT_EMIT_EQ(reporter, *array, "[42 .5]");
+    assert_emit_eq(reporter, *array, "[42 .5]");
 
     array->appendInt(0);
-    ASSERT_EMIT_EQ(reporter, *array, "[42 .5 0]");
+    assert_emit_eq(reporter, *array, "[42 .5 0]");
 
     array->appendBool(true);
-    ASSERT_EMIT_EQ(reporter, *array, "[42 .5 0 true]");
+    assert_emit_eq(reporter, *array, "[42 .5 0 true]");
 
     array->appendName("ThisName");
-    ASSERT_EMIT_EQ(reporter, *array, "[42 .5 0 true /ThisName]");
+    assert_emit_eq(reporter, *array, "[42 .5 0 true /ThisName]");
 
     array->appendName(SkString("AnotherName"));
-    ASSERT_EMIT_EQ(reporter, *array, "[42 .5 0 true /ThisName /AnotherName]");
+    assert_emit_eq(reporter, *array, "[42 .5 0 true /ThisName /AnotherName]");
 
     array->appendString("This String");
-    ASSERT_EMIT_EQ(reporter, *array,
+    assert_emit_eq(reporter, *array,
                    "[42 .5 0 true /ThisName /AnotherName (This String)]");
 
     array->appendString(SkString("Another String"));
-    ASSERT_EMIT_EQ(reporter, *array,
+    assert_emit_eq(reporter, *array,
                    "[42 .5 0 true /ThisName /AnotherName (This String) "
                    "(Another String)]");
 
     sk_sp<SkPDFArray> innerArray(new SkPDFArray);
     innerArray->appendInt(-1);
     array->appendObject(std::move(innerArray));
-    ASSERT_EMIT_EQ(reporter, *array,
+    assert_emit_eq(reporter, *array,
                    "[42 .5 0 true /ThisName /AnotherName (This String) "
                    "(Another String) [-1]]");
 
@@ -286,23 +290,23 @@
     array->appendObjRef(std::move(referencedArray));
 
     SkString result = emit_to_string(*array, &catalog);
-    ASSERT_EQ(reporter, result,
+    assert_eq(reporter, result,
               "[42 .5 0 true /ThisName /AnotherName (This String) "
               "(Another String) [-1] 1 0 R]");
 }
 
 static void TestPDFDict(skiatest::Reporter* reporter) {
     sk_sp<SkPDFDict> dict(new SkPDFDict);
-    ASSERT_EMIT_EQ(reporter, *dict, "<<>>");
+    assert_emit_eq(reporter, *dict, "<<>>");
 
     dict->insertInt("n1", SkToSizeT(42));
-    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 42>>");
+    assert_emit_eq(reporter, *dict, "<</n1 42>>");
 
     dict.reset(new SkPDFDict);
-    ASSERT_EMIT_EQ(reporter, *dict, "<<>>");
+    assert_emit_eq(reporter, *dict, "<<>>");
 
     dict->insertInt("n1", 42);
-    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 42>>");
+    assert_emit_eq(reporter, *dict, "<</n1 42>>");
 
     dict->insertScalar("n2", SK_ScalarHalf);
 
@@ -310,37 +314,37 @@
     sk_sp<SkPDFArray> innerArray(new SkPDFArray);
     innerArray->appendInt(-100);
     dict->insertObject(n3, std::move(innerArray));
-    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 42\n/n2 .5\n/n3 [-100]>>");
+    assert_emit_eq(reporter, *dict, "<</n1 42\n/n2 .5\n/n3 [-100]>>");
 
     dict.reset(new SkPDFDict);
-    ASSERT_EMIT_EQ(reporter, *dict, "<<>>");
+    assert_emit_eq(reporter, *dict, "<<>>");
 
     dict->insertInt("n1", 24);
-    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24>>");
+    assert_emit_eq(reporter, *dict, "<</n1 24>>");
 
     dict->insertInt("n2", SkToSizeT(99));
-    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99>>");
+    assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99>>");
 
     dict->insertScalar("n3", SK_ScalarHalf);
-    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5>>");
+    assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5>>");
 
     dict->insertName("n4", "AName");
-    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName>>");
+    assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName>>");
 
     dict->insertName("n5", SkString("AnotherName"));
-    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
+    assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
                    "/n5 /AnotherName>>");
 
     dict->insertString("n6", "A String");
-    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
+    assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
                    "/n5 /AnotherName\n/n6 (A String)>>");
 
     dict->insertString("n7", SkString("Another String"));
-    ASSERT_EMIT_EQ(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
+    assert_emit_eq(reporter, *dict, "<</n1 24\n/n2 99\n/n3 .5\n/n4 /AName\n"
                    "/n5 /AnotherName\n/n6 (A String)\n/n7 (Another String)>>");
 
     dict.reset(new SkPDFDict("DType"));
-    ASSERT_EMIT_EQ(reporter, *dict, "<</Type /DType>>");
+    assert_emit_eq(reporter, *dict, "<</Type /DType>>");
 
     sk_sp<SkPDFArray> referencedArray(new SkPDFArray);
     Catalog catalog;
@@ -349,7 +353,7 @@
                             referencedArray.get()) == 1);
     dict->insertObjRef("n1", std::move(referencedArray));
     SkString result = emit_to_string(*dict, &catalog);
-    ASSERT_EQ(reporter, result, "<</Type /DType\n/n1 1 0 R>>");
+    assert_eq(reporter, result, "<</Type /DType\n/n1 1 0 R>>");
 }
 
 DEF_TEST(PDFPrimitives, reporter) {
@@ -465,7 +469,7 @@
         ERRORF(reporter, "unscannable result: %s", floatString);
         return;
     }
-    if (isfinite(inputFloat) && roundTripFloat != inputFloat) {
+    if (std::isfinite(inputFloat) && roundTripFloat != inputFloat) {
         ERRORF(reporter, "roundTripFloat (%.9g) != inputFloat (%.9g)",
                roundTripFloat, inputFloat);
     }