質問

単純なプロジェクトの場合、大きな数値(4294967123など)を読み取り可能にする必要があるため、プレフィックス(4294967123-> 4.29G、12345-> 12.34Kなど)を付けて最初の数字のみを記述しています

コード(簡略化)は次のようになります。

const char* postfixes=" KMGT";
char postfix(unsigned int x)
{
     return postfixes[(int) floor(log10(x))];
}

動作しますが、完全な精度の対数を計算し、それを丸めて再びintにキャストするよりもエレガントで優れたソリューションがあると思います。

私が考えたその他の解決策:

int i=0;
for(; x >= 1000 ; ++i) x/=1000;
return postfixes[i];

(これは大幅に遅くなりますが、読みやすくなります)

数値はベンフォードの法則に従って分配され、数値は10 ^ x付近で丸めエラーが発生しないため、符号なし64ビット数値として扱われる必要があります(例:python math.log(1000,10 )は2.999996を返し、2)に切り捨てられます。 私が見逃している他の高速で正確な方法はありますか?

役に立ちましたか?

解決

log10 / floorコードは完全に読み取り可能であり、そのパフォーマンスコストは、後で出力で実行する文字列フォーマットのパフォーマンスコストによってlikely小化される可能性があります。

ただし、パフォーマンスが本当に必要な場合は する必要があるとします...

log10(x)== log2(x)/ log2(10)== log2(x)* 1 / log2(10)

1 / log2(10)は定数です

log2(x)は通常、CLZやビット調整ハック。64ビット整数に対して0〜63の数値を生成します。これは6ビットに収まり、基数ポイントの後の最大58ビットを64ビットタイプの固定小数点演算に使用できます。

したがって、固定小数点演算を使用してlog10を見つけることができます:

unsigned long long integer_log10( unsigned long long _in )
{
    unsigned long long log10fp6x58 = 0x134413509f79ff0llu; // (unsigned long long) (double(1llu<<58) / log2(10.0))
    return (((integer_log2(_in)) * log10fp6x58)+(1llu<<57)) >> 58;
}

integer_log2の実装はコンパイラ/プラットフォームに依存します。例えばGCC / PowerPCでは、

unsigned long long integer_log2( unsigned long long _in )
{
    return 63 - __cntlzd(_in);
}

このアプローチは、任意の底の対数を見つけるために一般化でき、上記のように適切な定数を計算するだけです。

他のヒント

これは、私が考えることができる最も簡単でシンプルな方法です...そしておそらく、対数を計算するよりも少し速いでしょう:

postfixes = {{1e12, "T"},
             {1e9,  "G"},
             {1e6,  "M"},
             {1e3,  "K"}}

for each postfix in postfixes{
    if(x > postfix.value){
        return (x / postfix.value) + postfix.letter;
    }
}

return x;

番号をいじるのではなく、代わりにs(n)printfで番号を &quot;%E&quot;を使用した文字列、E + 00 E + 03 E + 09を適切に置換 (など)(IIRC、科学表記法でのみ3のべき乗を取得する必要があります -これはあなたが望むものです。)

char number_buff[30];
snprintf(number_buff, 29, "%E", x);
char *powered_number_string = substitute_powers(number_buff);

char * substitute_powers(const char * number_buff)はCでは乱雑です。

sedは次のようなものです

-e s / E + 0 // -e s / E + 3 / K / -e s / E + 6 / M / -e s / E + 9 / G /

数値を文字列に変換し、文字列の長さを使用します。これは確かに高速ではありませんが、非常に正確です。その後、文字列を直接使用して、適切にスライスして結果を作成できます。

まず、ゼロをフォーマットする必要がある場合、その対数を取りたくありません。第二に、あなたはきれいなものが欲しいので、例えば「1000M」は望みません。 999,800,000。第三に、おそらく丸めが必要です。

この擬似コードのようなものを使用することをお勧めします:


function format(long x by value)
int p=5, char suf
if x<100000 then return string(x)
if x>=10000000000000 then
   x/=100000000
   p+=8
if x>=1000000000 then
   x/=10000
   p+=4
if x>=10000000 then
   x/=100
   p+=2
if x>=1000000 then
   x/=10
   p+=1
x+=5
if x>=100000 then
   x/=10
   p+=1
switch(p/3)
   6: suf='E'
   5: suf='P'
   4: suf='T'
   3: suf='G'
   2: suf='M'
   1: suf='K'
switch(p mod 3)
   2: return format("000 A",x/1000,suf)
   1: return format("00.0 A",x/10000,(x%10000)/100,suf)
   0: return format("0.00 A",x/100000,(x%100000)/100,suf)
end function
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top