#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <vector>
#include <iomanip>
#include <cmath>
using namespace std;
#define LIKELY(expr) (__builtin_expect(!!(expr), 1))
#define UNLIKELY(expr) (__builtin_expect(!!(expr), 0))
bool abscmp(double a, double b)
{
if (isnan(a) && isnan(b)) return true;
if (isnan(a) ^ isnan(b)) return false;
return a == b;
}
template <typename T>
inline __attribute__((always_inline)) T ParseFloat(const char *a) {
static constexpr T multers[] = {
0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, 0.00000001, 0.000000001, 0.0000000001, 0.00000000001,
0.000000000001, 0.0000000000001, 0.00000000000001, 0.000000000000001, 0.0000000000000001, 0.00000000000000001
};
static_assert(std::is_floating_point_v<T>);
int i = (a[0] == '-') | (a[0] == '+');
T res = 0.0;
int sign = 1 - 2 * (a[0] == '-');
if (UNLIKELY(!a[0])) return NAN;
while (a[i] && a[i] != '.') {
if (UNLIKELY(a[i] < '0' || a[i] > '9')) {
return NAN;
}
res = res * static_cast<T>(10.0) + a[i] - '0';
i++;
}
if (LIKELY(a[i] != '\0')) {
i++;
int j = i;
//T mult = 0.1;
while (a[i]) {
if (UNLIKELY(a[i] < '0' || a[i] > '9')) {
return NAN;
}
res = res + (a[i] - '0') * multers[i - j];
// res = res + (a[i] - '0') * mult;
// mult *= 0.1;
i++;
}
}
return res * sign;
}
int main()
{
string inputs[] = {
"31.0911863667",
"30.9500",
"225.1293333333",
"16.4850",
"29.0507297346",
"147.9440517474",
"28.8500",
"213.4600",
"212.9105553333",
"199.1553333333",
"19.5884123000",
"3092458.37500000000"
};
int n = sizeof(inputs) / sizeof(inputs[0]);
for (int i = 0; i < n; i++) {
float res1 = std::atof(inputs[i].c_str());
float res2 = ParseFloat<double>(inputs[i].c_str());
if (!abscmp(res1, res2)) {
cout << std::fixed << std::setprecision(20) << "CompareConvert " << res1 << " " << res2 << " " << std::string(inputs[i]) << std::endl;
} else {
cout << std::fixed << std::setprecision(20) << "Correct " << res1 << std::endl;
}
}
}
字符串
我正在编写一个简单的解析器(具有完整的有效性检查),因为std::atof
太慢(在我的测试输入中,ParseFast
平均快3.2倍-解析CSV文件的GB)。公式很简单,res = res * 10 + (a[i] - '0');
。但它给出了略有不同的结果。
我知道这是因为IEEE-754浮点的限制。但是有没有什么便宜的方法可以让ParseFast
给予和std::atof
完全相同的结果呢?我需要它们完全相同,因为它与使用sha 256 sum而不是fabs(a - b) < epsilon
检查相等性的遗留模块进行交互
要运行的命令:g++ -o main main.cpp -O3 -std=c++17
,gcc 10.2.0
**编辑:**上次输入错误原因解释:小数部分0.375
正好在2个可能值0.5
和0.25
之间。但是使用这种解析方法,在数字.3
处,由于舍入,中间结果将是3092458.0 + 0.3 == 3092458.25
。将0.075
添加到其中仍然会给予3092458.25
。
2条答案
按热度按时间fdbelqdn1#
我推荐使用fast_float库(https://github.com/fastfloat/fast_float)。据称它比
strtod
快4到10倍(glibc的atod
只是strtod
的一个简单 Package )。vhmi4jdf2#
正如您已经正确注意到的,问题是中间结果已经不精确,并且这会增加。例如,中间存储
.2
已经是不精确的,即使它后面可能跟着5
,这将使它成为.25
,这将是完全可表示的。您需要将浮点数的小数部分累加为整数(仍然是浮点数,但没有小数部分),然后在末尾除以一次以调整指数:
字符串
即使有了这些变化,使用第三方库可能更好,正如@cpplearner所推荐的那样。像
std::strtof
或std::to_chars
这样的标准库函数可能无法提供最佳性能,并且性能会因标准库而异。虽然您的解决方案在某些平台上可能会更快,但在浮点乘法和除法更昂贵的平台上,它的性能可能会差得多。浮点数很难。