C++標準ライブラリには、浮動小数点ログ方式しかありませんでした。ここで、ログを使用して、バイナリツリー(floor(2log(index))
)内のインデックスのレベルを見つけます。
コード(C++):
int targetlevel = int(log(index)/log(2));
一部のEdge要素(値が2 ^ nの要素)では、ログがn.0ではなくn-1.999999999999を返すのが怖いです。この恐怖は正しいですか?常に正しい答えを返すようにステートメントを変更するにはどうすればよいですか?
代わりにこのメソッドを使用できます。
int targetlevel = 0;
while (index >>= 1) ++targetlevel;
注:これにより、インデックスが変更されます。変更せずに必要な場合は、別の一時intを作成します。
インデックスが0の場合は例外です。おそらく、インデックス== 0の場合は、個別にチェックして例外をスローするか、エラーを返す必要があります。
最近のようなx86またはx86-64プラットフォームを使用している場合(おそらくそうです)、bsr
命令を使用して、符号なし整数の最上位セットビットの位置を返します。これは、log2()とまったく同じであることがわかります。以下は、インラインASMを使用してbsr
を呼び出す短いCまたはC++関数です。
#include <stdint.h>
static inline uint32_t log2(const uint32_t x) {
uint32_t y;
asm ( "\tbsr %1, %0\n"
: "=r"(y)
: "r" (x)
);
return y;
}
高速整数ログが必要な場合2 操作では、次の関数mylog2()
は、浮動小数点の精度を心配することなくそれを行います。
#include <limits.h>
static unsigned int mylog2 (unsigned int val) {
if (val == 0) return UINT_MAX;
if (val == 1) return 0;
unsigned int ret = 0;
while (val > 1) {
val >>= 1;
ret++;
}
return ret;
}
#include <stdio.h>
int main (void) {
for (unsigned int i = 0; i < 20; i++)
printf ("%u -> %u\n", i, mylog2(i));
putchar ('\n');
for (unsigned int i = 0; i < 10; i++)
printf ("%u -> %u\n", i+UINT_MAX-9, mylog2(i+UINT_MAX-9));
return 0;
}
上記のコードには小さなテストハーネスも含まれているため、動作を確認できます。
0 -> 4294967295
1 -> 0
2 -> 1
3 -> 1
4 -> 2
5 -> 2
6 -> 2
7 -> 2
8 -> 3
9 -> 3
10 -> 3
11 -> 3
12 -> 3
13 -> 3
14 -> 3
15 -> 3
16 -> 4
17 -> 4
18 -> 4
19 -> 4
4294967286 -> 31
4294967287 -> 31
4294967288 -> 31
4294967289 -> 31
4294967290 -> 31
4294967291 -> 31
4294967292 -> 31
4294967293 -> 31
4294967294 -> 31
4294967295 -> 31
入力値が0の場合、未定義の結果を示すUINT_MAX
を返すので、チェックする必要があります(有効な符号なし整数には対数がそれほど高くありません)。
ちなみに、 here から利用できる、これを正確に行うための非常に高速なハックがあります(2の補数で設定された最上位ビットを見つけます)。速度が重要でない限り、それらを使用することはお勧めしません(私自身は読みやすさを好みます)が、それらが存在することを認識させる必要があります。
64ビットの符号なし整数に対して私が行うことは次のとおりです。これにより、2を底とする対数の下限が計算されます。これは、最上位ビットのインデックスに相当します。このメソッドは、log²64= 6ステップで常に実行される展開されたループを使用するため、大きな数値の場合はスモーキング高速です。
基本的には、シーケンス{0≤k≤5:2 ^(2 ^ k)} = {2³²、2¹⁶、2⁸、2⁴、2²、2¹} = {4294967296、65536、256 、16、4、2、1}で、減算された値の指数kを合計します。
_int uint64_log2(uint64_t n)
{
#define S(k) if (n >= (UINT64_C(1) << k)) { i += k; n >>= k; }
int i = -(n == 0); S(32); S(16); S(8); S(4); S(2); S(1); return i;
#undef S
}
_
0の無効な入力が与えられた場合、これは-1を返すことに注意してください(これは最初の-(n == 0)
がチェックしているものです)。 _n == 0
_で呼び出すことを期待しない場合は、イニシャライザーの代わりに_int i = 0;
_を使用し、関数のエントリでassert(n != 0);
を追加できます。
10を底とする整数の対数は、同様に使用して計算できます。テストする最大の2乗は10¹⁶です。log₁₀2square≅19.2659 ...
_int uint64_log10(uint64_t n)
{
#define S(k, m) if (n >= UINT64_C(m)) { i += k; n /= UINT64_C(m); }
int i = -(n == 0);
S(16,10000000000000000); S(8,100000000); S(4,10000); S(2,100); S(1,10);
return i;
#undef S
}
_
使用している数式の浮動小数点の精度に問題はありませんでした(および1から2までの数字の簡単なチェック31 -1はエラーを検出しませんでした)が、心配な場合は代わりにこの関数を使用できます。同じ結果を返し、テストでは約66%高速です:
int HighestBit(int i){
if(i == 0)
return -1;
int bit = 31;
if((i & 0xFFFFFF00) == 0){
i <<= 24;
bit = 7;
}else if((i & 0xFFFF0000) == 0){
i <<= 16;
bit = 15;
}else if((i & 0xFF000000) == 0){
i <<= 8;
bit = 23;
}
if((i & 0xF0000000) == 0){
i <<= 4;
bit -= 4;
}
while((i & 0x80000000) == 0){
i <<= 1;
bit--;
}
return bit;
}
C++ 11を使用している場合、これをconstexpr関数にできます。
constexpr std::uint32_t log2(std::uint32_t n)
{
return (n > 1) ? 1 + log2(n >> 1) : 0;
}
int targetIndex = floor(log(i + 0.5)/log(2.0));
上記と同様の回答があります。この答え
関数:
static int floorLog2(int64_t x)
{
assert(x > 0);
return 63 - __builtin_clzl(x);
}
static int ceilLog2(int64_t x)
{
if (x == 1)
// On my system __builtin_clzl(0) returns 63. 64 would make more sense
// and would be more consistent. According to stackoverflow this result
// can get even stranger and you should just avoid __builtin_clzl(0).
return 0;
else
return floorLog2(x-1) + 1;
}
テストコード:
for (int i = 1; i < 35; i++)
std::cout<<"floorLog2("<<i<<") = "<<floorLog2(i)
<<", ceilLog2("<<i<<") = "<<ceilLog2(i)<<std::endl;
これは標準ではないか、必ずしも移植性はありませんが、一般的に機能します。それがどれほど効率的かはわかりません。
整数インデックスを十分な精度の浮動小数点数に変換します。精度が十分であれば、表現は正確になります。
IEEE浮動小数点数の表現を検索し、指数を抽出し、2を底とするログを見つけるために必要な調整を行います。
この関数は、数値間隔を表すために必要なビット数を決定します:[0..maxvalue]。
_unsigned binary_depth( unsigned maxvalue )
{
int depth=0;
while ( maxvalue ) maxvalue>>=1, depth++;
return depth;
}
_
結果から1を引くと、floor(log2(x))
が得られます。これは、x
の場合、log2(x)
のexact表現です。 2の累乗です。
xyy-1
-1
11
221
321
432
532
632
732
84
あなたのツリーをどのくらい深く投影しますか? say ... +/- 0.00000001の範囲を数値に設定して、強制的に整数値にすることができます。
Log2は2 ^ nの値を計算するときに精度を失わないため(浮動小数点は最も近い2の累乗に丸められるため)、1.99999999のような数値にヒットするかどうかは実際にはわかりません。
私が書いたこの関数 ここ
// The 'i' is for int, there is a log2 for double in stdclib
inline unsigned int log2i( unsigned int x )
{
unsigned int log2Val = 0 ;
// Count Push off bits to right until 0
// 101 => 10 => 1 => 0
// which means hibit was 3rd bit, its value is 2^3
while( x>>=1 ) log2Val++; // div by 2 until find log2. log_2(63)=5.97, so
// take that as 5, (this is a traditional integer function!)
// eg x=63 (111111), log2Val=5 (last one isn't counted by the while loop)
return log2Val ;
}
書き換えTodd Lehmanの答えをより一般的にする:
#include <climits>
template<typename N>
constexpr N ilog2(N n) {
N i = 0;
for (N k = sizeof(N) * CHAR_BIT; 0 < (k /= 2);) {
if (n >= static_cast<N>(1) << k) { i += k; n >>= k; }
}
return i;
}
-O3
を含むClangはループを展開します。
0000000100000f50 pushq %rbp
0000000100000f51 movq %rsp, %rbp
0000000100000f54 xorl %eax, %eax
0000000100000f56 cmpl $0xffff, %edi
0000000100000f5c setg %al
0000000100000f5f shll $0x4, %eax
0000000100000f62 movl %eax, %ecx
0000000100000f64 sarl %cl, %edi
0000000100000f66 xorl %edx, %edx
0000000100000f68 cmpl $0xff, %edi
0000000100000f6e setg %dl
0000000100000f71 leal (,%rdx,8), %ecx
0000000100000f78 sarl %cl, %edi
0000000100000f7a leal (%rax,%rdx,8), %eax
0000000100000f7d xorl %edx, %edx
0000000100000f7f cmpl $0xf, %edi
0000000100000f82 setg %dl
0000000100000f85 leal (,%rdx,4), %ecx
0000000100000f8c sarl %cl, %edi
0000000100000f8e leal (%rax,%rdx,4), %eax
0000000100000f91 xorl %edx, %edx
0000000100000f93 cmpl $0x3, %edi
0000000100000f96 setg %dl
0000000100000f99 leal (%rdx,%rdx), %ecx
0000000100000f9c sarl %cl, %edi
0000000100000f9e leal (%rax,%rdx,2), %ecx
0000000100000fa1 xorl %eax, %eax
0000000100000fa3 cmpl $0x1, %edi
0000000100000fa6 setg %al
0000000100000fa9 orl %ecx, %eax
0000000100000fab popq %rbp
n
が定数の場合、結果はコンパイル時に計算されます。
浮動小数点数が機能する方法(厳密には仮数* 2 ^指数)を考えると、2のべき乗である最大2 ^ 127までの数値はエラーなしで正確に表現されます。
これは些細ではあるがかなりハックな解決策を提供します-浮動小数点数のビットパターンを整数として解釈し、指数を見てください。これは、上記のDavid Thornleyのソリューションです。
float f = 1;
for (int i = 0; i < 128; i++)
{
int x = (*(int*)(&f)>>23) - 127;
int l = int(log(f) / log(2));
printf("i = %d, log = %d, f = %f quick = %d\n",
i, l, f, x);
f *= 2;
}
any integerがfloatとして表現できることは正しくありません-仮数よりもビット数が少ないものだけが表現できます。 32ビットの浮動小数点数では、23ビットの価値があります。
これは古い投稿ですが、1行のアルゴリズムを共有しています。
unsigned uintlog2(unsigned x)
{
unsigned l;
for(l=0; x>1; x>>=1, l++);
return l;
}