2

我正在尝试编写一个(大部分)* C 程序来对数值结果进行排序并消除重复项。结果存储为 STRUCTS,其中包含一个字符串、一个整数和 4 个双精度数。双打与确定两个结果是否重复有关。

为此,我使用 4 个双精度来 sprintf 一个字符串,以达到一定的精度,即

    #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>. 然后程序检查哈希表是否已经包含该键的条目,如果是,则结果是重复的并且可以丢弃。否则,它会被添加到哈希表中。

问题是有时我的一个值会被 sprintf 从例如 -10E-9 舍入为零;结果,字符串将包含“-0.00000”而不是“0.00000”。尽管代表相同的结果,这两个值显然会生成不同的哈希键。

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;
    }
4

5 回答 5

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,以便对几乎相同的数字进行稳定排序。

于 2011-07-07T02:34:14.363 回答
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));

这也需要对浮点值的大小进行限制。你不想溢出你的整数值。

正如其他人所建议的那样,您还可以绕过字符串格式并简单地将舍入计算作为结构级哈希的一部分进行。

于 2011-07-07T04:16:57.510 回答
1

也许实现一个实用函数来将值四舍五入/捕捉到正零。使用类似于 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"

这旨在为希望避免格式化字符串中的负零的人们提供通用解决方案。不特定于原始发布者使用创建密钥或散列。

于 2020-03-26T01:59:23.280 回答
0

如果您仅将其用于散列双精度值,则不必费心将它们转换为字符串 - 只需直接散列双精度值即可。任何值得称道的哈希库都可以散列任意二进制数据块。

如果出于某种奇怪的原因,您的哈希库仅支持以 null 结尾的 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

这确保了不相等的值肯定会被赋予不相等的字符串。

于 2011-07-07T02:39:24.640 回答
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;
}
于 2011-07-07T12:52:15.010 回答