英文:
c++ float to string
问题
C++是否有任何机制(函数或其他方式)将浮点数(或双精度浮点数)转换为既保持数字精度又保持数字长度最小的表示形式?我的意思是类似JavaScript的方式。
看起来C++似乎不愿意这样做,例如:
- std::to_string(1.23456789e10); // 返回"12345678900.000000"(多余的零)
- (std::ostringstream() << 1.23456789e10).str() // 返回"1.234567e10"(丢失了两位数字)
- snprintf(buffer, sizeof(buffer), "%g", 1.23456789e10); // 与上述相同
英文:
Does C++ have ANY mechanism (function or whatever) to convert a float (or double) to the representation that maintains both precision of a number and also a minimal length of the number? I mean something like JavaScript does.
It looks like C++ is not willing to do so, for example:
- std::to_string(1.23456789e10); // returns "12345678900.000000" (unnecessary zeros)
- (std::ostringstream() << 1.23456789e10).str() // returns "1.234567e10" (two digits lost)
- snprintf(buffer, sizeof(buffer), "%g", 1.23456789e10); // as above
答案1
得分: -1
The answer is not that obvious but actually Yes. Here I share the code that converts fundamental types to std::string maintaining precision and relatively short representation of float and double types.
template <typename Type>
constexpr static std::string toString(Type value) noexcept
{
std::ostringstream oss;
if constexpr (std::is_floating_point<typename std::remove_cv<Type>::type>::value)
oss << std::defaultfloat << std::setprecision(std::numeric_limits<Type>::digits10 + 1);
oss << value;
return oss.str();
}
Now some background. The code above is based on std::ostringstream
. You may think why the above implementation is better than std::to_string
for example? Moreover, why not other methods as C++ provides actually a lot of different methods offering similar stuff. Ok then, lets walk through each method:
std::string
: First of all it may produce invalid outputs. Especially for small values offloat
anddouble
source. Moreover it appends lots of unnecessary zeros for example for 1.23456789e10 input the function produces "12345678900.000000".snprintf
with "%g" specifier. It produces either "%f" or "%e" whatever is shorter. Unfortunately reduces precision while it could be preserved. For example, the 1.23456789e10 produces "1.234567e10". Also if you'd manually specify the precision, it will either add unnecessary zeros or remove less significant digits. We would like to have something that automatically maintains the precision and short length of the stringstd::format
: Omg, I was waiting years for this function. Unfortunately, as of today only MSVC and clang implements theheader and it is still missing in g++. std::to_chars
: Feature of C++23, not existing yet. Looks like another remake ofstd::to_string
.- Any use of third party libraries - came on! For just a such purpose it would be an overkill.
std::ostringstream
by itself does not keep the accuracy of a number. For example the 1.23456789e10 produces "1.234567e10". It requires to specify std::setprecision
along with the std::defaultfloat
to maintain the precision and relatively short (user friendly) representation. For more information please refer to the cppreference.com. And it is not that obvious in general - for more information please refer to the source.
Important note: The code is not exactly maintaining the precision as stated above. This was done on purpose. If you look carefully the std::numeric_limits<Type>::digits10
is "number of decimal digits that can be represented without change" while std::numeric_limits<Type>::max_digits10
is "number of decimal digits necessary to differentiate all values of this type". Therefore, you need by your own decide what exactly you need. In case you are presenting a number to the user the digits10
is the most "user friendly" representation (0.3f is "0.3" for float), however only max_digits10
will give you the exact representation (0.3f is "0.300000012" for float). Thank you Eric for indicating this!
英文:
The answer is not that obvious but actually Yes. Here I share the code that converts fundamental types to std::string maintaining precision and relatively short representation of float and double types.
template <typename Type>
constexpr static std::string toString(Type value) noexcept
{
std::ostringstream oss;
if constexpr (std::is_floating_point<typename std::remove_cv<Type>::type>::value)
oss << std::defaultfloat << std::setprecision(std::numeric_limits<Type>::digits10 + 1);
oss << value;
return oss.str();
}
Now some background. The code above is based on std::ostringstream
. You may think why the above implementation is better than std::to_string
for example? Moreover, why not other methods as C++ provides actually a lot of different methods offering similar stuff. Ok then, lets walk through each method:
std::string
: First of all it may produce invalid outputs. Especially for small values offloat
anddouble
source. Moreover it appends lots of unnecessary zeros for example for 1.23456789e10 input the function produces "12345678900.000000".snprintf
with"%g"
specifier. It produces either"%f"
or"%e"
whatever is shorter. Unfortunately reduces precision while it could be preserved. For example, the 1.23456789e10 produces "1.234567e10". Also if you'd manually specify the precision, it will either add unnecessary zeros or remove less significant digits. We would like to have something that automatically maintains the precision and short length of the stringstd::format
: Omg, I was waiting years for this function. Unfortunately, as of today only MSVC and clang implements the <format> header and it is still missing in g++.std::to_chars
: Feature of C++23, not existing yet. Looks like another remake ofstd::to_string
.- Any use of third party libraries - came on! For just a such purpose it would be an overkill.
std::ostringstream
by itself does not keep the accuracy of a number. For example the 1.23456789e10 produces "1.234567e10". It requires to specify std::setprecision
along with the std::defaultfloat
to maintain the precision and relatively short (user friendly) representation. For more information please refer to the cppreference.com. And it is not that obvious in general - for more information please refer to the source.
Important note: The code is not exactly maintaining the precision as stated above. This was done on purpose. If you look carefully the std::numeric_limits<Type>::digits10
is "number of decimal digits that can be represented without change" while std::numeric_limits<Type>::max_digits10
is "number of decimal digits necessary to differentiate all values of this type". Therefore, you need by your own decide what exactly you need. In case you are presenting a number to the user the digits10
is the most "user friendly" representation (0.3f is "0.3" for float), however only max_digits10
will give you the exact representation (0.3f is "0.300000012" for float). Thank you Eric for indicating this!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论