2009-07-30 2 views
2

単純なプロジェクトでは、大きな数字(4294967123など)を読みやすくする必要があるため、接頭辞(4294967123 - > 4.29G、12345 - > 12.34Kなど)の最初の桁のみを書いています。 。)ラウンドオーダーの計算

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

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

それは動作しますが、私は、完全な精度の対数を計算し、それを丸めし、それをダウンキャストするよりもエレガント/よりよい解決策があることを考えます再び整数。私が考え

他のソリューション:

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

(これはかなり遅いですが、読みやすくするために)

番号はベンフォードの法則と数に応じての間に分布しているが、符号なし64として扱われるべきです10^xの近くに丸め誤差がないはずです(例えば、Python math.log(1000,10)は2.999996を返し、2になります)。 私は行方不明の高速、正確な他の方法はありますか?

+0

。 x << = 3; – Drakosha

+2

"整数の整数ログベース2を見つける(別名最高ビットセットの位置)" http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup – Drakosha

+0

ああ!これは、有名なゲーム_Taipanを思い出させます!_(これをプレイしましたか?)_Taipan!_対数を使用しました。 –

答えて

16

しかし、log10の(X)== LOG2(X)/ LOG2(10)== LOG2(X)* 1/LOG2こと

注...パフォーマンスを必要とする本当にあなたがにしたと仮定(10)

1/LOG2(10)一定

LOG2(X)は、通常0の間の数を得、そのようなCLZよう命令またはbit twiddling hackを使用して近代的なアーキテクチャ上の整数パイプラインで安価に行うことが可能です64ビット整数の場合は63です。これは6ビットに収まり、64ビットタイプの固定小数点演算に使用できる基点の後で58ビットまでになります。

だから我々は、その後の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); 
} 

だこのアプローチは、上記のように単に適切な定数を計算し、任意の塩基対数を求めるために一般化することができます。

+0

変更する方がいいでしょう: 符号なしlong long log10fp6x58 => static const unsigned long long log10fp6x58 – Drakosha

+0

Clang/LLVMとIntelの最新のセットアップでどうすればいいですか? (OS X) –

+1

これは良い解決策ですが、1/log_2_(10)の近似は結果が正しいことが保証されていないことを意味します。 [1、10^3、10^6など]と一致する[""、 "M"、 "G"、...]の静的配列を含むことが、 。]。次に、実行時にバイナリ検索を使用して、最初の配列に最も近いものを検索し、charの2番目の配列にインデックスを付けます。 – Isaac

0

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

+0

重要なポイントで四捨五入するのはどうですか? – paul

2

これは私が考えることができる最も簡単かつ簡単な方法です...そして多分それは少し速く対数を計算するよりも次のようになります。

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

は、sが(代わりに、番号をいじるしないでくださいn) "%E"を使用して 文字列に数値を出力し、次に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)

-es/E + 0 // -es/E + 3/K/-es/E + 6 /ようなものであろうsedのC.

に厄介ですあなたのlog10 /フロアコードは完全に読み込み可能であり、そのパフォーマンスコストは、あなたの出力で後に実行される文字列フォーマットのそれに比べて小さくなりすぎる可能性があります。M/-es/E + 9/G/

+0

それは動作しますが、それはforループよりも遅く、最初は理解するのが非常に難しいです。 – tstenner

0

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

私はあなたがこの擬似コードのようなものを使用することを示唆している:私はあなたがX/= 1000置き換えようとすべきだと思う


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