簡體   English   中英

獲取 printf 以忽略零值上的負號

[英]Get printf to ignore the negative sign on values of zero

我正在嘗試編寫一個(大部分)* C 程序來對數值結果進行排序並消除重復項。 結果存儲為包含字符串、integer 和 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<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;
    }

與其 sprintf() 將雙精度數轉換為一個大字符串並將其用作 map 中的鍵,不如將結構體放入 map 中? 如果您只是為您的結構編寫一個小於運算符,該運算符將您想要用作鍵的浮點值視為您可以很容易地做到這一點。 像這樣的東西:

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

然后你可以用std::map<ResultType>替換你的tr1::unordered_map<string, ResultType> > ,並且一起避免整個字符串打印業務。 如果您願意,可以在比較 function 中添加一些 epsilon,以便對幾乎相同的數字進行穩定排序。

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

這也需要對浮點值的大小進行限制。 您不想溢出您的 integer 值。

正如其他人所建議的那樣,您還可以繞過字符串格式,只需將舍入計算作為結構級 hash 的一部分進行。

也許實現一個實用程序 function 將值舍入/捕捉到正零。 使用類似於 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));

測試 output:

"0, -0.05, -0.0490"

這旨在為希望避免格式化字符串中的負零的人們提供通用解決方案。 不特定於原始發布者使用創建密鑰或 hash。

如果您僅將其用於散列雙精度值,則不要費心將它們轉換為字符串 - 只需 hash 直接使用雙精度值即可。 任何有價值的 hash 庫都將能夠 hash 任意二進制數據塊。

如果出於某種奇怪的原因,您的 hash 庫僅支持以 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

這確保了不相等的值肯定會被賦予不相等的字符串。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM