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