2011-07-07 24 views
1

我正在嘗試編寫一個(主要)* C程序,用於對數值結果進行排序並消除重複項。結果存儲爲包含字符串,整數和4個雙打的STRUCTS。雙打是確定兩個結果是否重複的相關信息。獲取printf以忽略零值的負號

要做到這一點,我使用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>一個hashkey sprintf的一個字符串。然後程序檢查哈希表是否已經包含該鍵的條目,如果是,結果是重複的並且可以被丟棄。否則,它會被添加到散列表。

問題是有時候我的一個值會被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; 
    } 
+0

也許應該用C++標記 - 畢竟,解決方案可能涉及tr1庫中的散列計算。一個可能的答案 - 寫一個自定義哈希函數,調用標準函數,但在轉發之前調整參數。但是,您可能還需要一個自定義運算符==來爲散列表檢查密鑰。 – Steve314

+0

添加了C++標記。儘管如果可能的話,我真的很想避免寫一個散列函數。 – BenB

+0

這可能會有點不利於開發大部分C語言,我只是想利用偶爾的C++庫。 – Steve314

回答

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; 
} 
+0

似乎與我的工作類似 - 但有一個epsilon。 epsilon是一個好主意。我認爲,我的解決方案應該是健壯的。任何人都看到任何潛在的問 – BenB

+0

編輯原始問題以顯示我的解決方法與epsilon。 – BenB

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到比較函數,以便幾乎相同的數字被穩定地排序。

+0

+1,但重新「如果你想要你可以添加一些epsilon的比較函數,使幾乎相同的數字穩定排序。」 - 他們將被「穩定地排序」而不包含epsilon,但是epsilons將解決他們被認爲是重複的要求...... –

+0

我對這個概念有點困惑(或者只是語法)。運算符應該返回一個布爾值來允許地圖進行排序,對嗎?所以如果第一個參數小於第二個參數,它將返回true,否則返回false。在等值或重複結果的情況下做了什麼?我也不確定OR運算符如何適合函數的返回。 – BenB

+1

@Tony:關於epsilons的好處。將epsilons添加到比較函數並將其保留爲部分排序並不重要。特別是,你真的想要一個== b和b == c暗示a == c時消除重複。如果不是,加入'{b,a,c}'會讓你只用'b',但加入'{a,c,b}'會讓你有'a'和'c'。 – MSalters

0

如果你只是爲了散列double值而使用它,那麼不要費心把它們轉換成字符串 - 直接散列double值。任何值得使用的哈希庫都可以散列任意二進制數據塊。

如果出於某種奇怪的原因,你的哈希庫只支持空終止的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 

這確保了不平等的值一定會給予不等的字符串。

+2

從Q「對數值結果進行排序並消除重複項」......在我看來,它是要求某個精度*定義了重複項的一部分,並且您繞過了所有這些...... –

+1

我認爲這會使我離開我相同或更大的問題。我仍然必須處理負的零,我也必須處理四捨五入。 – BenB

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

這也需要對浮點值的大小綁定。你不想溢出你的整數值。

也可以繞過字符串格式,並像其他人所建議的那樣,簡單地將舍入計算作爲結構級哈希的一部分。

+0

整數是否爲零? – BenB

+0

正確。負零是IEEE浮點值的屬性(因爲符號位完全獨立於其他所有東西,所以可以有任何正/負)。 – Tom

+0

感謝您的建議。我喜歡它,而且我最初設置了它,我認爲這可能是最好的事情。非常感謝,我會牢記這一點。 – BenB