// Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "google/protobuf/io/strtod.h" #include // FLT_DIG and DBL_DIG #include #include #include #include #include #include #include // NOLINT(build/c++11) #include "absl/log/absl_check.h" #include "absl/strings/charconv.h" #include "absl/strings/numbers.h" #include "absl/strings/str_format.h" namespace google { namespace protobuf { namespace io { // This approximately 0x1.ffffffp127, but we don't use 0x1.ffffffp127 because // it won't compile in MSVC. constexpr double MAX_FLOAT_AS_DOUBLE_ROUNDED = 3.4028235677973366e+38; float SafeDoubleToFloat(double value) { // static_cast on a number larger than float can result in illegal // instruction error, so we need to manually convert it to infinity or max. if (value > std::numeric_limits::max()) { // Max float value is about 3.4028234664E38 when represented as a double. // However, when printing float as text, it will be rounded as // 3.4028235e+38. If we parse the value of 3.4028235e+38 from text and // compare it to 3.4028234664E38, we may think that it is larger, but // actually, any number between these two numbers could only be represented // as the same max float number in float, so we should treat them the same // as max float. if (value <= MAX_FLOAT_AS_DOUBLE_ROUNDED) { return std::numeric_limits::max(); } return std::numeric_limits::infinity(); } else if (value < -std::numeric_limits::max()) { if (value >= -MAX_FLOAT_AS_DOUBLE_ROUNDED) { return -std::numeric_limits::max(); } return -std::numeric_limits::infinity(); } else { return static_cast(value); } } double NoLocaleStrtod(const char *str, char **endptr) { double ret = 0.0; // This isn't ideal, but the existing function interface does not provide any // bounds. const char *end = strchr(str, 0); auto result = absl::from_chars(str, end, ret); // from_chars() with DR 3081's current wording will return max() on // overflow. SimpleAtod returns infinity instead. if (result.ec == std::errc::result_out_of_range) { if (ret > 1.0) { ret = std::numeric_limits::infinity(); } else if (ret < -1.0) { ret = -std::numeric_limits::infinity(); } } if (endptr) { *endptr = const_cast(result.ptr); } return ret; } // ---------------------------------------------------------------------- // SimpleDtoa() // SimpleFtoa() // We want to print the value without losing precision, but we also do // not want to print more digits than necessary. This turns out to be // trickier than it sounds. Numbers like 0.2 cannot be represented // exactly in binary. If we print 0.2 with a very large precision, // e.g. "%.50g", we get "0.2000000000000000111022302462515654042363167". // On the other hand, if we set the precision too low, we lose // significant digits when printing numbers that actually need them. // It turns out there is no precision value that does the right thing // for all numbers. // // Our strategy is to first try printing with a precision that is never // over-precise, then parse the result with strtod() to see if it // matches. If not, we print again with a precision that will always // give a precise result, but may use more digits than necessary. // // An arguably better strategy would be to use the algorithm described // in "How to Print Floating-Point Numbers Accurately" by Steele & // White, e.g. as implemented by David M. Gay's dtoa(). It turns out, // however, that the following implementation is about as fast as // DMG's code. Furthermore, DMG's code locks mutexes, which means it // will not scale well on multi-core machines. DMG's code is slightly // more accurate (in that it will never use more digits than // necessary), but this is probably irrelevant for most users. // // Rob Pike and Ken Thompson also have an implementation of dtoa() in // third_party/fmt/fltfmt.cc. Their implementation is similar to this // one in that it makes guesses and then uses strtod() to check them. // Their implementation is faster because they use their own code to // generate the digits in the first place rather than use snprintf(), // thus avoiding format string parsing overhead. However, this makes // it considerably more complicated than the following implementation, // and it is embedded in a larger library. If speed turns out to be // an issue, we could re-implement this in terms of their // implementation. // ---------------------------------------------------------------------- namespace { // In practice, doubles should never need more than 24 bytes and floats // should never need more than 14 (including null terminators), but we // overestimate to be safe. constexpr int kDoubleToBufferSize = 32; constexpr int kFloatToBufferSize = 24; inline bool IsValidFloatChar(char c) { return ('0' <= c && c <= '9') || c == 'e' || c == 'E' || c == '+' || c == '-'; } void DelocalizeRadix(char *buffer) { // Fast check: if the buffer has a normal decimal point, assume no // translation is needed. if (strchr(buffer, '.') != nullptr) return; // Find the first unknown character. while (IsValidFloatChar(*buffer)) ++buffer; if (*buffer == '\0') { // No radix character found. return; } // We are now pointing at the locale-specific radix character. Replace it // with '.'. *buffer = '.'; ++buffer; if (!IsValidFloatChar(*buffer) && *buffer != '\0') { // It appears the radix was a multi-byte character. We need to remove the // extra bytes. char *target = buffer; do { ++buffer; } while (!IsValidFloatChar(*buffer) && *buffer != '\0'); memmove(target, buffer, strlen(buffer) + 1); } } bool safe_strtof(const char *str, float *value) { char *endptr; errno = 0; // errno only gets set on errors *value = strtof(str, &endptr); return *str != 0 && *endptr == 0 && errno == 0; } char *FloatToBuffer(float value, char *buffer) { // FLT_DIG is 6 for IEEE-754 floats, which are used on almost all // platforms these days. Just in case some system exists where FLT_DIG // is significantly larger -- and risks overflowing our buffer -- we have // this assert. static_assert(FLT_DIG < 10, "FLT_DIG_is_too_big"); if (value == std::numeric_limits::infinity()) { absl::SNPrintF(buffer, kFloatToBufferSize, "inf"); return buffer; } else if (value == -std::numeric_limits::infinity()) { absl::SNPrintF(buffer, kFloatToBufferSize, "-inf"); return buffer; } else if (std::isnan(value)) { absl::SNPrintF(buffer, kFloatToBufferSize, "nan"); return buffer; } int snprintf_result = absl::SNPrintF(buffer, kFloatToBufferSize, "%.*g", FLT_DIG, value); // The snprintf should never overflow because the buffer is significantly // larger than the precision we asked for. ABSL_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize); float parsed_value; if (!safe_strtof(buffer, &parsed_value) || parsed_value != value) { snprintf_result = absl::SNPrintF(buffer, kFloatToBufferSize, "%.*g", FLT_DIG + 3, value); // Should never overflow; see above. ABSL_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize); } DelocalizeRadix(buffer); return buffer; } char *DoubleToBuffer(double value, char *buffer) { // DBL_DIG is 15 for IEEE-754 doubles, which are used on almost all // platforms these days. Just in case some system exists where DBL_DIG // is significantly larger -- and risks overflowing our buffer -- we have // this assert. static_assert(DBL_DIG < 20, "DBL_DIG_is_too_big"); if (value == std::numeric_limits::infinity()) { absl::SNPrintF(buffer, kDoubleToBufferSize, "inf"); return buffer; } else if (value == -std::numeric_limits::infinity()) { absl::SNPrintF(buffer, kDoubleToBufferSize, "-inf"); return buffer; } else if (std::isnan(value)) { absl::SNPrintF(buffer, kDoubleToBufferSize, "nan"); return buffer; } int snprintf_result = absl::SNPrintF(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG, value); // The snprintf should never overflow because the buffer is significantly // larger than the precision we asked for. ABSL_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize); // We need to make parsed_value volatile in order to force the compiler to // write it out to the stack. Otherwise, it may keep the value in a // register, and if it does that, it may keep it as a long double instead // of a double. This long double may have extra bits that make it compare // unequal to "value" even though it would be exactly equal if it were // truncated to a double. volatile double parsed_value = NoLocaleStrtod(buffer, nullptr); if (parsed_value != value) { snprintf_result = absl::SNPrintF(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG + 2, value); // Should never overflow; see above. ABSL_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize); } DelocalizeRadix(buffer); return buffer; } } // namespace std::string SimpleDtoa(double value) { char buffer[kDoubleToBufferSize]; return DoubleToBuffer(value, buffer); } std::string SimpleFtoa(float value) { char buffer[kFloatToBufferSize]; return FloatToBuffer(value, buffer); } } // namespace io } // namespace protobuf } // namespace google