让 printf 忽略零值上的负号

发布于 2024-11-19 02:34:30 字数 1867 浏览 7 评论 0原文

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

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

问题是有时我的值之一会被 sprintf 从 -10E-9 四舍五入到零;因此,该字符串将包含“-0.00000”而不是“0.00000”。尽管表示相同的结果,但这两个值显然会生成不同的哈希键。

sprintf 甚至 C 语言中是否有内置的东西可以让我处理这个问题?我想出了一些解决办法(参见下面的帖子)——但如果有内置的东西,我宁愿使用它。

*该程序是用 C 编写的,因为这是我最熟悉的语言,但我最终将使用 g++ 进行编译以便使用 unordered_map。

我想出了以下解决方法。但是A)我希望有一个内置的解决方案,B)我对atof或浮点数学没有很深入的理解,所以我不确定条件是否if(doubleRepresentation == 0.0)< /code> 总会在该跳闸的时候跳闸。

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

I'm trying to write a (mostly)* C program that sorts numerical results and eliminates duplicates. The results are stored as STRUCTS that contain a string, an integer, and 4 doubles. The doubles are what is relevant for determining if two results are duplicates.

To do this, I sprintf a string using the 4 doubles to some precision i.e.

    #define PRECISION 5
sprintf(hashString, "%.*lf %.*lf %.*lf %.*lf", PRECISION, result.v1, PRECISION, result.v2, PRECISION, result.v3, PRECISION, result.v4);

I then use this as a hashkey for a tr1::unordered_map<string, ResultType>. Then the program checks to see if the hashtable already contains an entry for that key, if so, the result is a duplicate and can be discarded. Otherwise, it gets added to the hashtable.

The problem is that sometimes one of my values will be rounded to zero from, for example, -10E-9, by sprintf; As a result, the string will contain "-0.00000" rather than "0.00000". These two values will obviously generate different hashkeys, despite representing the same result.

Is there something built into sprintf or even the C language that will allow me to deal with this? I've come up with a bit of a work around (see post below) -- but if there's something built in, I would much rather use that.

*the program is written in C because that's the language I'm most comfortable in, but I'll end up compiling with g++ in order to use the unordered_map.

I've come up with the following workaround. But A) I'm hoping there's a builtin solution and B) I don't have a very deep understanding of atof or floating point math, so I'm not sure if the condition if(doubleRepresentation == 0.0) will always trip when it should.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

世界如花海般美丽 2024-11-26 02:34:30

与其将双精度数 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 替换为 std::map,并避免整个字符串打印业务。如果需要,可以在比较函数中添加一些 epsilon,以便几乎相同的数字可以稳定排序。

Rather than sprintf()ing the doubles to a big string and using that as the key in a map, why not just put your structs into the map? You can do this easily enough if you just write a less-than operator for your structs which considers the floating-point values you want to use as the key. Something like this:

bool operator <(const MyStruct &lhs, const MyStruct &rhs)
{
    return lhs.v1 < rhs.v1 ||
        (lhs.v1 == rhs.v1 && lhs.v2 < rhs.v2); // ...
}

Then you can replace your tr1::unordered_map<string, ResultType> with std::map<ResultType>, and avoid the whole string printing business all together. If you want you can add some epsilon to the comparison function so that numbers that are nearly the same are stably sorted.

那片花海 2024-11-26 02:34:30

如果您知道您只关心 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));

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

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

If you know that you only care about differences of 0.00001 (based on your definition of PRECISION), you can round the values to integers first. Something like this may work:

#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));

This also requires a bound on the magnitude of the floating-point values. You don't want to overflow your integer values.

You can also bypass the string formatting and simply do the rounding calculations as part of a structure-level hash, as others have suggested.

分開簡單 2024-11-26 02:34:30

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

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

Perhaps implement a utility function to round/snap values to positive zero. Use precision digit count similar to printf style format syntax.

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

Test:

f32 value = -0.049f;
int precision = 4; // Test precision from param
printf("%.0f, %.2f, %.*f", PosZero(value), PosZero(value,2), precision, PosZero(value,precision));

Test output:

"0, -0.05, -0.0490"

This is intended to be a general solution for people wanting to avoid negative zeros in formatted strings. Not specific to the original poster's use of creating a key or hash.

秉烛思 2024-11-26 02:34:30
#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;
}
#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;
}
許願樹丅啲祈禱 2024-11-26 02:34:30

如果您仅将其用于散列双精度值,则不必费心将它们转换为字符串 - 只需直接散列双精度值即可。任何有价值的哈希库都能够对任意二进制数据进行哈希处理。

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

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

If you're only using this for the purposes of hashing the double values, then don't bother converting them to a string—just hash the double values directly. Any hash library worth its salt will have the ability to hash arbitrary binary blobs of data.

If for some strange reason your hash library only supports null-terminated C strings, then print out the raw bytes of the double value:

// 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

This ensures that unequal values will definitely be given unequal strings.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文