如何格式化浮点值,使其从不使用指数表示法,也不具有尾随零
How do I format a floating point value so that it never uses exponent notation nor has trailing zeros?
根据ios_base操纵器,在格式化不带指数表示法(带十进制数)的浮点数时,我基本上可以在defaultfloat
和fixed
之间进行选择。
然而,我想选择最大精度,这将为许多数字(例如1.
)的fixed
产生许多尾随零,但避免使用指数表示法。如果设置为defaultfloat
,它在大多数时间内看起来都是正确的,除非该值真的很小,但不是0.
。在这种情况下,默认表示法会自行切换到科学表示法,这会破坏格式化输出的接收器(因为它不知道2.22045e-16
的意思
那么,我怎么能既吃馅饼又吃馅饼呢?也就是说,没有不必要的尾随零的非指数表示法。
注意:我没有测试defaultfloat
标志的效果,因为我的gcc似乎还没有实现那个标志,但我认为它是默认设置,不使用任何标志即可应用。我确实检查了fixed
标志,它的行为与预期的一样
一个简单的方法如下:
std::string float2string(double f)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(122) << f; // 122 is LARGE, but you may find that really tiny or really large numbers still don't work out...
std::string s = ss.str();
std::string::size_type len = s.length();
int zeros = 0;
while(len > 1 && s[--len] == '0')
zeros++;
if (s[len] == '.') // remove final '.' if number ends with '.'
zeros++;
s.resize(s.length()-zeros);
return s;
}
我对它进行了一些测试。最大的问题是,它为一些数字提供了大量的小数,而像0.05这样的数字则是0.05000000000000000277555756156289135105907917022705078125
0.7变为:0.05000000000000000277555756156289135105907917022705078125
这是因为它不是二进制形式的"精确"数字
我认为解决方案是通过取整数位数(或floor(log(f)/log(10)+1)),然后从常数中减去这个数字,比如17,这是你可以从双位数中得到的位数。然后去掉多余的零。
我暂时不谈,因为我还有其他事情要做,我应该在不久前开始…;)
由于似乎没有一种正确的方法来处理stdlib,所以这里是我的包装器。
template <typename T>
struct FpFormat {
template <typename Stream>
static Stream& setfmt(Stream& str) {
return str;
}
template <typename String>
static String const& untrail(String const& str) {
return str;
}
};
template <typename T>
struct FpFormatFloats {
template <typename Stream>
static auto setfmt(Stream& str) -> decltype(str << std::fixed << std::setprecision(std::numeric_limits<T>::digits10)) {
return str << std::fixed << std::setprecision(std::numeric_limits<T>::digits10);
}
template <typename String>
static String untrail(String str) {
if (str.find('.') == String::npos)
return str;
return ([](String s){
return String(s.begin(),s.begin()+((s.back() == '.')?(s.size()-1):s.size()));
})(str.substr(0,(str+"0").find_last_not_of('0')+1));
}
};
template <> struct FpFormat<float> : FpFormatFloats<float> {};
template <> struct FpFormat<double> : FpFormatFloats<double> {};
template <> struct FpFormat<long double> : FpFormatFloats<long double> {};
template <typename T>
std::string toString(T x) {
std::stringstream str;
FpFormat<T>::setfmt(str) << x;
return FpFormat<T>::untrail(str.str());
}
遗憾的是,在iostream
或printf()
中似乎都没有这样做的代码。我的意思是:
- 以固定格式打印
- 打印最小位数,以便编码是无损的(即,最接近的有效浮点数字是您打印的数字)
您不能仅以非常高的精度打印并去掉尾随的零,因为可能存在不必要的非零数字。
我相信真正做到这一点的唯一好方法是使用像Ryu这样的专用代码。
另一种方法是以非常高的精度打印下一个和上一个数字,并取小数点后相同的数字(加一)。不过会很难看的。
没有内置的格式可以为您做到这一点。您可以从头开始生成自己的格式化程序,也可以编写一个对字符串进行后处理以删除不必要的尾随零的格式化程序。
例如,您可以使用正则表达式替换:
#include <iomanip>
#include <iostream>
#include <sstream>
#include <regex>
// not thoroughly tested
std::string format(double d, int precision = 100) {
std::stringstream ss;
ss << std::fixed << std::setprecision(precision);
ss << d;
return std::regex_replace(
std::regex_replace(ss.str(), std::regex(R"((-?d*.d*?)0*$)"), "$1"),
std::regex(R"(.$)"), "");
}
int main() {
std::cout << format(-1) << 'n';
std::cout << format(1e20) << 'n';
std::cout << format(1e-20) << 'n';
std::cout << format(2.22045e-16) << 'n';
}
尽管使用正则表达式可能不是一个特别有效的解决方案。
- 表示"accepting anything for this template argument" C++概念的通配符
- 如何将ampl中的集合表示为c++中的向量
- 使用简单类型列表实现的指数编译时间.为什么
- std::is_base_of表示ctor编译错误
- 输入中的字符串数未知(以字母表示)
- 我可以信任表示整数的浮点或双精度来保持精度吗
- c++模板来表示多项式
- 询问在设计我的手臂模拟器功能表示格式1
- CMakeLists.txt中的命名空间表示法
- C++射线示踪剂ppm表示没有足够的数据来显示图像
- 如何计算Big-O表示法中的平均渐近运行时间
- 我应该如何表示我拥有的连续元素序列?
- 在C++中,使用带有 std::optional 参数的函数<T>来表示可选参数是否有意义?
- 在 std::无符号字符的向量处存储 int 的十六进制表示形式
- 表示类模板C++空类型
- 双到字符串用指数表示的大数字的转换
- 阅读 CPP 中的 Fortran 指数表示法
- 从字符串中解析指数表示法中的整数
- 如何格式化浮点值,使其从不使用指数表示法,也不具有尾随零
- 用指数的二进制表示的模幂