Home | History | Annotate | Download | only in utils
      1 /*
      2  * Copyright 2017 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkFloatToDecimal.h"
      9 
     10 #include <cfloat>
     11 #include <climits>
     12 #include <cmath>
     13 
     14 #include "SkTypes.h"
     15 
     16 // Return pow(10.0, e), optimized for common cases.
     17 static double pow10(int e) {
     18     switch (e) {
     19         case 0:  return 1.0;  // common cases
     20         case 1:  return 10.0;
     21         case 2:  return 100.0;
     22         case 3:  return 1e+03;
     23         case 4:  return 1e+04;
     24         case 5:  return 1e+05;
     25         case 6:  return 1e+06;
     26         case 7:  return 1e+07;
     27         case 8:  return 1e+08;
     28         case 9:  return 1e+09;
     29         case 10: return 1e+10;
     30         case 11: return 1e+11;
     31         case 12: return 1e+12;
     32         case 13: return 1e+13;
     33         case 14: return 1e+14;
     34         case 15: return 1e+15;
     35         default:
     36             if (e > 15) {
     37                 double value = 1e+15;
     38                 while (e-- > 15) { value *= 10.0; }
     39                 return value;
     40             } else {
     41                 SkASSERT(e < 0);
     42                 double value = 1.0;
     43                 while (e++ < 0) { value /= 10.0; }
     44                 return value;
     45             }
     46     }
     47 }
     48 
     49 /** Write a string into result, includeing a terminating '\0' (for
     50     unit testing).  Return strlen(result) (for SkWStream::write) The
     51     resulting string will be in the form /[-]?([0-9]*.)?[0-9]+/ and
     52     sscanf(result, "%f", &x) will return the original value iff the
     53     value is finite. This function accepts all possible input values.
     54 
     55     Motivation: "PDF does not support [numbers] in exponential format
     56     (such as 6.02e23)."  Otherwise, this function would rely on a
     57     sprintf-type function from the standard library. */
     58 unsigned SkFloatToDecimal(float value, char result[kMaximumSkFloatToDecimalLength]) {
     59     /* The longest result is -FLT_MIN.
     60        We serialize it as "-.0000000000000000000000000000000000000117549435"
     61        which has 48 characters plus a terminating '\0'. */
     62 
     63     static_assert(kMaximumSkFloatToDecimalLength == 49, "");
     64     // 3 = '-', '.', and '\0' characters.
     65     // 9 = number of significant digits
     66     // abs(FLT_MIN_10_EXP) = number of zeros in FLT_MIN
     67     static_assert(kMaximumSkFloatToDecimalLength == 3 + 9 - FLT_MIN_10_EXP, "");
     68 
     69     /* section C.1 of the PDF1.4 spec (http://goo.gl/0SCswJ) says that
     70        most PDF rasterizers will use fixed-point scalars that lack the
     71        dynamic range of floats.  Even if this is the case, I want to
     72        serialize these (uncommon) very small and very large scalar
     73        values with enough precision to allow a floating-point
     74        rasterizer to read them in with perfect accuracy.
     75        Experimentally, rasterizers such as pdfium do seem to benefit
     76        from this.  Rasterizers that rely on fixed-point scalars should
     77        gracefully ignore these values that they can not parse. */
     78     char* output = &result[0];
     79     const char* const end = &result[kMaximumSkFloatToDecimalLength - 1];
     80     // subtract one to leave space for '\0'.
     81 
     82     /* This function is written to accept any possible input value,
     83        including non-finite values such as INF and NAN.  In that case,
     84        we ignore value-correctness and and output a syntacticly-valid
     85        number. */
     86     if (value == INFINITY) {
     87         value = FLT_MAX;  // nearest finite float.
     88     }
     89     if (value == -INFINITY) {
     90         value = -FLT_MAX;  // nearest finite float.
     91     }
     92     if (!std::isfinite(value) || value == 0.0f) {
     93         // NAN is unsupported in PDF.  Always output a valid number.
     94         // Also catch zero here, as a special case.
     95         *output++ = '0';
     96         *output = '\0';
     97         return static_cast<unsigned>(output - result);
     98     }
     99     if (value < 0.0) {
    100         *output++ = '-';
    101         value = -value;
    102     }
    103     SkASSERT(value >= 0.0f);
    104 
    105     int binaryExponent;
    106     (void)std::frexp(value, &binaryExponent);
    107     static const double kLog2 = 0.3010299956639812;  // log10(2.0);
    108     int decimalExponent = static_cast<int>(std::floor(kLog2 * binaryExponent));
    109     int decimalShift = decimalExponent - 8;
    110     double power = pow10(-decimalShift);
    111     SkASSERT(value * power <= (double)INT_MAX);
    112     int d = static_cast<int>(value * power + 0.5);
    113     // SkASSERT(value == (float)(d * pow(10.0, decimalShift)));
    114     SkASSERT(d <= 999999999);
    115     if (d > 167772159) {  // floor(pow(10,1+log10(1<<24)))
    116        // need one fewer decimal digits for 24-bit precision.
    117        decimalShift = decimalExponent - 7;
    118        // SkASSERT(power * 0.1 = pow10(-decimalShift));
    119        // recalculate to get rounding right.
    120        d = static_cast<int>(value * (power * 0.1) + 0.5);
    121        SkASSERT(d <= 99999999);
    122     }
    123     while (d % 10 == 0) {
    124         d /= 10;
    125         ++decimalShift;
    126     }
    127     SkASSERT(d > 0);
    128     // SkASSERT(value == (float)(d * pow(10.0, decimalShift)));
    129     unsigned char buffer[9]; // decimal value buffer.
    130     int bufferIndex = 0;
    131     do {
    132         buffer[bufferIndex++] = d % 10;
    133         d /= 10;
    134     } while (d != 0);
    135     SkASSERT(bufferIndex <= (int)sizeof(buffer) && bufferIndex > 0);
    136     if (decimalShift >= 0) {
    137         do {
    138             --bufferIndex;
    139             *output++ = '0' + buffer[bufferIndex];
    140         } while (bufferIndex);
    141         for (int i = 0; i < decimalShift; ++i) {
    142             *output++ = '0';
    143         }
    144     } else {
    145         int placesBeforeDecimal = bufferIndex + decimalShift;
    146         if (placesBeforeDecimal > 0) {
    147             while (placesBeforeDecimal-- > 0) {
    148                 --bufferIndex;
    149                 *output++ = '0' + buffer[bufferIndex];
    150             }
    151             *output++ = '.';
    152         } else {
    153             *output++ = '.';
    154             int placesAfterDecimal = -placesBeforeDecimal;
    155             while (placesAfterDecimal-- > 0) {
    156                 *output++ = '0';
    157             }
    158         }
    159         while (bufferIndex > 0) {
    160             --bufferIndex;
    161             *output++ = '0' + buffer[bufferIndex];
    162             if (output == end) {
    163                 break;  // denormalized: don't need extra precision.
    164                 // Note: denormalized numbers will not have the same number of
    165                 // significantDigits, but do not need them to round-trip.
    166             }
    167         }
    168     }
    169     SkASSERT(output <= end);
    170     *output = '\0';
    171     return static_cast<unsigned>(output - result);
    172 }
    173