質問
単純なプロジェクトの場合、大きな数値(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