我正在尝试编写一个(大部分)* C程序,它可以对数值结果进行排序并消除重复数据。结果存储为STRUCTS,其中包含字符串,整数和4个双精度数。双打是确定两个结果是否重复的相关内容。
为此,我使用4个双精度打印一个字符串到一些精度,即
#define PRECISION 5
sprintf(hashString, "%.*lf %.*lf %.*lf %.*lf", PRECISION, result.v1, PRECISION, result.v2, PRECISION, result.v3, PRECISION, result.v4);
然后我将其用作tr1::unordered_map<string, ResultType>
的哈希键。然后程序检查哈希表是否已经包含该键的条目,如果是,则结果是重复的并且可以被丢弃。否则,它会被添加到哈希表中。
问题在于,有时我的一个值将从例如-10E-9舍入为零,由sprintf;因此,该字符串将包含“-0.00000”而不是“0.00000”。尽管表示相同的结果,但这两个值显然会生成不同的hashkey。
sprintf甚至是C语言中都有内容可以让我处理这个问题吗?我想出了一些解决方法(见下面的帖子) - 但如果有内置的东西,我宁愿使用它。
*程序是用C语言编写的,因为这是我最熟悉的语言,但我最终会用g ++编译才能使用unordered_map。
我提出了以下解决方法。但是A)我希望有一个内置的解决方案和B)我对atof或浮点数学没有非常深刻的理解,所以我不确定条件if(doubleRepresentation == 0.0)
是否总是会跳到它应该。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define PRECISION 5
#define ACCURACY 10E-6
double getRidOfNegZeros (double number)
{
char someNumAsStr[PRECISION + 3]; // +3 accounts for a possible minus sign, the leading 0 or 1, and the decimal place.
sprintf(someNumAsStr, "%.*lf", PRECISION, number);
double doubleRepresentation = atof(someNumAsStr);
if((doubleRepresentation < ACCURACY) && (doubleRepresentation > -ACCURACY))
{
doubleRepresentation = 0.0;
}
return doubleRepresentation;
}
int main()
{
printf("Enter a number: \n");
double somenum;
scanf("%lf",&somenum);
printf("The new representation of double \"%.*lf\" is \"%.*lf\"\n", PRECISION, somenum, PRECISION, getRidOfNegZeros(somenum));
return 0;
}
答案 0 :(得分:2)
为什么不将你的结构放入地图中,而不是将双打sprintf()转换为大字符串并将其用作地图中的关键字?如果你只为结构编写一个小于运算符,你可以很容易地做到这一点,结构考虑了你想要用作键的浮点值。像这样:
bool operator <(const MyStruct &lhs, const MyStruct &rhs)
{
return lhs.v1 < rhs.v1 ||
(lhs.v1 == rhs.v1 && lhs.v2 < rhs.v2); // ...
}
然后,您可以将tr1::unordered_map<string, ResultType>
替换为std::map<ResultType>
,并避免整个字符串打印业务。如果您愿意,可以在比较函数中添加一些epsilon,以便对几乎相同的数字进行稳定排序。
答案 1 :(得分:1)
如果您知道自己只关心0.00001的差异(基于PRECISION
的定义),则可以先将值四舍五入为整数。这样的事情可能有用:
#include <math.h>
#include <stdio.h>
#define SCALE 1e5 // instead of PRECISION 5
sprintf(hashString, "%d %d %d %d",
(int)round(result.v1 * SCALE),
(int)round(result.v2 * SCALE),
(int)round(result.v3 * SCALE),
(int)round(result.v4 * SCALE));
这也需要对浮点值的大小进行限制。您不希望溢出整数值。
您也可以绕过字符串格式化,只需将舍入计算作为结构级哈希的一部分,正如其他人所建议的那样。
答案 2 :(得分:0)
如果您只是为了散列双值而使用它,那么不要费心将它们转换为字符串 - 只需直接散列双值。任何值得盐的哈希库都能够散列任意二进制blob数据。
如果出于某些奇怪的原因,您的哈希库仅支持以空值终止的C字符串,则打印出double
值的原始字节:
// Alias the double value as a byte array
unsigned char *d = (unsigned char *)&result.v1;
// Prefer snprintf to sprintf!
spnrintf(hashString, hashStringLength, "%02x%02x%02x%02x%02x%02x%02x%02x",
d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]);
// ...and so on for each double value
这确保了不相等的值肯定会被赋予不相等的字符串。
答案 3 :(得分:0)
#include <string>
#define PRECISION 5
#define LIMIT 5e-6
std::string string_rep (double x) {
char buf[32];
double xtrunc = ((x > -LIMIT) && (x < LIMIT)) ? 0.0 : x;
std::sprintf (buf, "%.*f", PRECISION, xtrunc);
return std::string(buf);
}
std::string make_key (double x, double y, double z, double w) {
std::string strx = string_rep (x);
std::string stry = string_rep (y);
std::string strz = string_rep (z);
std::string strw = string_rep (w);
return strx + " " + stry + " " + strz + " " + strw;
}
答案 4 :(得分:0)
也许实现了一个实用函数,将值四舍五入/捕捉到正零。使用类似于printf样式格式语法的精确数字计数。
// Prevent display of -0 values by snapping to positive zero
// \a_number original number
// \a_precisionCount number of digits of decimal precision eg. 2 for #.##, 0 for whole integer. Default 0 (whole integer number.)
// \returns number rounded to positive zero if result would have produced -0.00 for precision.
template <class Real>
Real PosZero(const Real& a_number, const int a_precisionCount = 0)
{
Real precisionValue = Real(0.5) * pow(Real(0.10), Real(a_precisionCount));
if( (a_number > -abs(precisionValue)) && (a_number < abs(precisionValue)) )
{
return +0.0;
}
return a_number;
}
测试:
f32 value = -0.049f;
int precision = 4; // Test precision from param
printf("%.0f, %.2f, %.*f", PosZero(value), PosZero(value,2), precision, PosZero(value,precision));
测试输出:
"0, -0.05, -0.0490"
这旨在为希望避免格式化字符串中的负零的人们提供一种通用解决方案。不特定于原始发布者使用的创建密钥或哈希值。