2

数値結果を並べ替えて重複を排除する (ほとんど)* C プログラムを作成しようとしています。結果は、文字列、整数、および 4 つの double を含む STRUCTS として格納されます。double は、2 つの結果が重複しているかどうかを判断するために関連するものです。

これを行うには、4 つの double を使用して文字列をある程度の精度でスプリントします。

    #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>ます。次に、プログラムは、ハッシュテーブルにそのキーのエントリが既に含まれているかどうかを確認します。含まれている場合、結果は重複しており、破棄できます。それ以外の場合は、ハッシュ テーブルに追加されます。

問題は、値の 1 つが sprintf によって、たとえば -10E-9 からゼロに丸められることがあることです。その結果、文字列には「0.00000」ではなく「-0.00000」が含まれます。これら 2 つの値は、同じ結果を表しているにもかかわらず、明らかに異なるハッシュキーを生成します。

これに対処できるようにするために、sprintf または C 言語に組み込まれているものはありますか? 私はちょっとした回避策を考え出しました (以下の投稿を参照)。ただし、何かが組み込まれている場合は、むしろそれを使用したいと思います。

* プログラムは C で書かれていますが、これは私が最も慣れている言語ですが、unordered_map を使用するために g++ でコンパイルすることになります。

次の回避策を考え出しました。しかし、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

double を大きな文字列に 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>次に、 yourをに置き換えてstd::map<ResultType>、文字列印刷ビジネス全体をまとめて回避できます。必要に応じて、比較関数にイプシロンを追加して、ほぼ同じ数値が安定してソートされるようにすることができます。

于 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 に答える
0

double 値をハッシュする目的でのみこれを使用している場合は、それらを文字列に変換する必要はありません。double 値を直接ハッシュするだけです。そのソルトに値するハッシュ ライブラリには、データの任意のバイナリ BLOB をハッシュする機能があります。

なんらかの奇妙な理由で、ハッシュ ライブラリが 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 に答える