// // NumericString.h // // Library: Foundation // Package: Core // Module: NumericString // // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // SPDX-License-Identifier: BSL-1.0 // #include "Poco/Bugcheck.h" // +++ double conversion +++ #define double_conversion poco_double_conversion // don't collide with standalone double_conversion library #define UNREACHABLE poco_bugcheck #define UNIMPLEMENTED poco_bugcheck #include "diy-fp.cc" #include "cached-powers.cc" #include "bignum-dtoa.cc" #include "bignum.cc" #include "fast-dtoa.cc" #include "fixed-dtoa.cc" #include "strtod.cc" #include "double-conversion.cc" // --- double conversion --- #include "Poco/NumericString.h" poco_static_assert(POCO_MAX_FLT_STRING_LEN == double_conversion::kMaxSignificantDecimalDigits); #include "Poco/String.h" #include #include namespace { void pad(std::string& str, int precision, int width, char prefix = ' ', char decSep = '.') /// Pads the string with prefix space and postfix 0. /// Alternative prefix (e.g. zero instead of space) can be supplied by caller. /// Used only internally. { // these cases should never happen, if they do, it's a library bug poco_assert_dbg (precision > 0); poco_assert_dbg (str.length()); std::string::size_type decSepPos = str.find(decSep); if (decSepPos == std::string::npos) { str.append(1, '.'); decSepPos = str.size() - 1; } std::string::size_type frac = str.length() - decSepPos - 1; std::string::size_type ePos = str.find_first_of("eE"); std::unique_ptr eStr; if (ePos != std::string::npos) { eStr.reset(new std::string(str.substr(ePos, std::string::npos))); frac -= eStr->length(); str = str.substr(0, str.length() - eStr->length()); } if (frac != precision) { if (frac < precision) { str.append(precision - frac, '0'); } else if ((frac > precision) && (decSepPos != std::string::npos)) { int pos = static_cast(decSepPos) + 1 + precision; if (str[pos] >= '5') // we must round up { char carry = 0; if(str[--pos] == '9') { str[pos] = '0'; carry = 1; } else { ++str[pos]; carry = 0; } while (--pos >= 0) { if(str[pos] == decSep) continue; if(carry) { if((str[pos] + carry) <= '9') { ++str[pos]; carry = 0; } else { str[pos] = '0'; carry = 1; } } } if (carry) str.insert(str.begin(), 1, '1'); } str = str.substr(0, decSepPos + 1 + precision); } } if (eStr.get()) str += *eStr; if (width && (str.length() < width)) str.insert(str.begin(), width - str.length(), prefix); } void insertThousandSep(std::string& str, char thSep, char decSep = '.') /// Inserts thousand separators. /// Used only internally. { poco_assert (decSep != thSep); if (str.size() == 0) return; std::string::size_type exPos = str.find('e'); if (exPos == std::string::npos) exPos = str.find('E'); std::string::size_type decPos = str.find(decSep); // there's no rinsert, using forward iterator to go backwards std::string::iterator it = str.end(); if (exPos != std::string::npos) it -= str.size() - exPos; if (decPos != std::string::npos) { while (it != str.begin()) { --it; if (*it == decSep) break; } } int thCount = 0; if (it == str.end()) --it; for (; it != str.begin();) { std::string::iterator pos = it; std::string::value_type chr = *it; std::string::value_type prevChr = *--it; if (!std::isdigit(chr)) continue; if (++thCount == 3 && std::isdigit(prevChr)) it = str.insert(pos, thSep); if (thCount == 3) thCount = 0; } } } // namespace namespace Poco { void floatToStr(char* buffer, int bufferSize, float value, int lowDec, int highDec) { using namespace double_conversion; StringBuilder builder(buffer, bufferSize); int flags = DoubleToStringConverter::UNIQUE_ZERO | DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, lowDec, highDec, 0, 0); dc.ToShortestSingle(value, &builder); builder.Finalize(); } void floatToFixedStr(char* buffer, int bufferSize, float value, int precision) { using namespace double_conversion; StringBuilder builder(buffer, bufferSize); int flags = DoubleToStringConverter::UNIQUE_ZERO | DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, -std::numeric_limits::digits10, std::numeric_limits::digits10, 0, 0); dc.ToFixed(value, precision, &builder); builder.Finalize(); } std::string& floatToStr(std::string& str, float value, int precision, int width, char thSep, char decSep) { if (!decSep) decSep = '.'; if (precision == 0) value = std::floor(value); char buffer[POCO_MAX_FLT_STRING_LEN]; floatToStr(buffer, POCO_MAX_FLT_STRING_LEN, value); str = buffer; if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) replaceInPlace(str, '.', decSep); if (thSep) insertThousandSep(str, thSep, decSep); if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.'); return str; } std::string& floatToFixedStr(std::string& str, float value, int precision, int width, char thSep, char decSep) { if (!decSep) decSep = '.'; if (precision == 0) value = std::floor(value); char buffer[POCO_MAX_FLT_STRING_LEN]; floatToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision); str = buffer; if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) replaceInPlace(str, '.', decSep); if (thSep) insertThousandSep(str, thSep, decSep); if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.'); return str; } void doubleToStr(char* buffer, int bufferSize, double value, int lowDec, int highDec) { using namespace double_conversion; StringBuilder builder(buffer, bufferSize); int flags = DoubleToStringConverter::UNIQUE_ZERO | DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, lowDec, highDec, 0, 0); dc.ToShortest(value, &builder); builder.Finalize(); } void doubleToFixedStr(char* buffer, int bufferSize, double value, int precision) { using namespace double_conversion; StringBuilder builder(buffer, bufferSize); int flags = DoubleToStringConverter::UNIQUE_ZERO | DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN; DoubleToStringConverter dc(flags, POCO_FLT_INF, POCO_FLT_NAN, POCO_FLT_EXP, -std::numeric_limits::digits10, std::numeric_limits::digits10, 0, 0); dc.ToFixed(value, precision, &builder); builder.Finalize(); } std::string& doubleToStr(std::string& str, double value, int precision, int width, char thSep, char decSep) { if (!decSep) decSep = '.'; if (precision == 0) value = std::floor(value); char buffer[POCO_MAX_FLT_STRING_LEN]; doubleToStr(buffer, POCO_MAX_FLT_STRING_LEN, value); str = buffer; if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) replaceInPlace(str, '.', decSep); if (thSep) insertThousandSep(str, thSep, decSep); if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.'); return str; } std::string& doubleToFixedStr(std::string& str, double value, int precision, int width, char thSep, char decSep) { if (!decSep) decSep = '.'; if (precision == 0) value = std::floor(value); char buffer[POCO_MAX_FLT_STRING_LEN]; doubleToFixedStr(buffer, POCO_MAX_FLT_STRING_LEN, value, precision); str = buffer; if (decSep && (decSep != '.') && (str.find('.') != std::string::npos)) replaceInPlace(str, '.', decSep); if (thSep) insertThousandSep(str, thSep, decSep); if (precision > 0 || width) pad(str, precision, width, ' ', decSep ? decSep : '.'); return str; } float strToFloat(const char* str, const char* inf, const char* nan) { using namespace double_conversion; int processed; int flags = StringToDoubleConverter::ALLOW_LEADING_SPACES | StringToDoubleConverter::ALLOW_TRAILING_SPACES; StringToDoubleConverter converter(flags, 0.0, Single::NaN(), inf, nan); float result = converter.StringToFloat(str, static_cast(strlen(str)), &processed); return result; } double strToDouble(const char* str, const char* inf, const char* nan) { using namespace double_conversion; int processed; int flags = StringToDoubleConverter::ALLOW_LEADING_SPACES | StringToDoubleConverter::ALLOW_TRAILING_SPACES; StringToDoubleConverter converter(flags, 0.0, Double::NaN(), inf, nan); double result = converter.StringToDouble(str, static_cast(strlen(str)), &processed); return result; } bool strToFloat(const std::string& str, float& result, char decSep, char thSep, const char* inf, const char* nan) { using namespace double_conversion; std::string tmp(str); trimInPlace(tmp); removeInPlace(tmp, thSep); removeInPlace(tmp, 'f'); replaceInPlace(tmp, decSep, '.'); result = strToFloat(tmp.c_str(), inf, nan); return !FPEnvironment::isInfinite(result) && !FPEnvironment::isNaN(result); } bool strToDouble(const std::string& str, double& result, char decSep, char thSep, const char* inf, const char* nan) { if (str.empty()) return false; using namespace double_conversion; std::string tmp(str); trimInPlace(tmp); removeInPlace(tmp, thSep); replaceInPlace(tmp, decSep, '.'); removeInPlace(tmp, 'f'); result = strToDouble(tmp.c_str(), inf, nan); return !FPEnvironment::isInfinite(result) && !FPEnvironment::isNaN(result); } } // namespace Poco