c / c ++でログbase(2)を書き込む方法


97

log(base 2)関数を書く方法はありますか?

C言語には2つの組み込み関数があります->>

1. logベースeです。

2. log10ベース10;

しかし、私はベース2のログ関数が必要です。これを計算する方法。


1
眼球計算では、2を底とする対数は、10を底とする対数に自然対数を加えたものにほぼ等しくなります。明らかに、プログラムでより正確な(そしてより速い)バージョンを書く方が良いです。
David Thornley

整数の場合、右ビットシフトでループし、0に達したときに停止することができます。ループカウントは、ログの近似値です
Basile Starynkevitch

回答:


197

簡単な数学:

    log 2x)= log yx)/ log y(2)

ここで、yは何でもかまいません。標準のログ関数の場合は10またはeです。



52

積分結果を探している場合は、値に設定されている最上位ビットを特定して、その位置を返すことができます。


26
(Javaのから取られ、このための素晴らしいビットいじる方法もありますInteger.highestOneBit(int)方法は):i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); i |= (i >> 8); i |= (i >> 16); return i - (i >>> 1);
ジョーイ

37
...またはwhile (i >>= 1) { ++l; }
Lee Daniel Crocker 2013

2
@Joeyそれは整数が32ビット幅であると仮定して動作します、いいえ?64ビットの場合は、追加になりi>>32ます。ただし、Javaには32ビットのintしかないため、問題ありません。C / C ++の場合は、考慮する必要があります。
ゾソ2017年

42
#define M_LOG2E 1.44269504088896340736 // log2(e)

inline long double log2(const long double x){
    return log(x) * M_LOG2E;
}

(乗算は除算よりも速い場合があります)


1
明確にしたかった-ログ変換ルールを使用+ log_2(e)= 1 / log_e(2)という事実->結果が得られる
Guy L


9

http://en.wikipedia.org/wiki/Logarithmに記載されているように:

logb(x) = logk(x) / logk(b)

つまり:

log2(x) = log10(x) / log10(2)

11
パフォーマンスを向上させるために、log10(2)を事前計算できることに注意してください。
corsiKa

@ヨハネス:コンパイラがlog10(2)を事前に計算することはないと思います。コンパイラーは、log10が毎回同じ値を返すことを認識していません。すべてのコンパイラーが知っているように、log10(2)は連続した呼び出しで異なる値を返す可能性があります。
アベレンキー

@abelenky:わかりました、私はそれを取り戻します。コンパイラはソースを決して見ないのでlog()実装それは行いません。私の悪い。
ジョーイ

3
@abelenky:log10()関数はC標準で定義されているため、コンパイラは結果を事前計算することを含め、「特別に」自由に処理できます。これは、@ Johannesの提案だったと思いますか?
ca

1
@CarlNorum:私がチェックしたところ、少なくともgcc 4.7 log10(2)は定数に置き換えられました。
カフェ

8

高速にしたい場合は、Bit Twiddling Hacksのようなルックアップテーブルを使用できます(整数log2のみ)。

uint32_t v; // find the log base 2 of 32-bit v
int r;      // result goes here

static const int MultiplyDeBruijnBitPosition[32] = 
{
  0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
  8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};

v |= v >> 1; // first round down to one less than a power of 2 
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;

r = MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27];

さらに、ハードウェアで完全に計算される可能性があるため、より高速に_BitScanReverseなる可能性のあるコンパイラ組み込みメソッドを確認する必要があります。

C ++で整数log2()を実行する方法についても確認してください。


最後に乗算とテーブルルックアップを実行する理由 次の2の累乗に切り上げるだけの(v + 1)を実行できませんか?そして、あなたは2のべき乗にラウンドダウンに1だけ右シフトする可能性が
Safayetアーメド

@SafayetAhmedその方法で数値のlog2を検索する方法を説明してください。その値を取得する簡単な方法はわかりません。上記の算術をルックアップテーブルで使用する以外に、反復/再帰アルゴリズムを使用するか、専用/組み込みハードウェアを使用して計算を行うことができます。
bkausbk

32ビット変数vのビットに0(LSB)からN(MSB)までの番号が付けられているとします。vの最上位セットビットがnであるとします。nがfloor(log2(v))を表すと言って間違いありませんか?与えられたvからnを見つけることに興味はありませんか?
Safayet Ahmed

私が説明したことは、実際の対数ではなく、最も近い2のべき乗を与えるだけであることに気付きました。乗算とテーブルルックアップは、2の累乗から対数に進むためのものです。数値0x07C4ACDDをいくらか左にシフトしています。左にシフトする量は、2の累乗に依存します。この数は、連続する5ビットのシーケンスが一意になるような数です。(0000 0111 1100 0100 0110 1100 1101 1101)は、シーケンス(00000)(00001)...(11101)を提供します。どれだけ左にシフトするかに応じて、これらの5ビットパターンの1つを取得します。次に、テーブルを検索します。非常に素晴らしい。
Safayet Ahmed 2015

3
log2(x) = log10(x) / log10(2)

シンプルさ、明快さ、そしてOPの与えられた情報に基づいたコードの提供のために賛成票を投じてください。
ヨーヨー2014

3
uint16_t log2(uint32_t n) {//but truncated
     if (n==0) throw ...
     uint16_t logValue = -1;
     while (n) {//
         logValue++;
         n >>= 1;
     }
     return logValue;
 }

基本的にはtomlogicと同じです。


1
このソリューションにはいくつかの問題点がありますが、一般的に、浮動小数点を避けたい場合はこれが適切です。符号なし整数を-1で初期化しているため、これが機能するためにはオーバーフローに依存しています。これを0に初期化してから値-1を返すことで修正できます。0の場合を確認するとします。もう1つの問題は、n == 0のときにループが停止することに依存していることです。それ以外は、浮動小数点を避けたい場合に最適です。
Rian Quinn

2

math.h(C)またはcmath(C ++)を含める必要があります。もちろん、私たちが知っている数学を実行する必要があることを覚えておいてください... 0より大きい数値のみです。

例:

#include <iostream>
#include <cmath>
using namespace std;

int main(){
    cout<<log2(number);
}

2

最上位ビットの位置よりも高い精度が必要であり、使用していたマイクロコントローラーには数学ライブラリがありませんでした。正の整数値の引数に2 ^ n値間の線形近似を使用するだけでうまくいくことがわかりました。これがコードです:

uint16_t approx_log_base_2_N_times_256(uint16_t n)
{
    uint16_t msb_only = 0x8000;
    uint16_t exp = 15;

    if (n == 0)
        return (-1);
    while ((n & msb_only) == 0) {
        msb_only >>= 1;
        exp--;
    }

    return (((uint16_t)((((uint32_t) (n ^ msb_only)) << 8) / msb_only)) | (exp << 8));
}

私のメインプログラムでは、整数の結果でN * log2(N)/ 2を計算する必要がありました。

temp =(((uint32_t)N)* approx_log_base_2_N_times_256)/ 512;

すべての16ビット値が2%以上ずれることはありません


1

上記の答えはすべて正しいです。以下の私の答えは、誰かがそれを必要とする場合に役立ちます。Cを使用して解決している多くの質問でこの要件を見てきました。

log2 (x) = logy (x) / logy (2)

ただし、C言語を使用していて、結果を整数にしたい場合は、以下を使用できます。

int result = (int)(floor(log(x) / log(2))) + 1;

お役に立てれば。


0

あなたの基本的な数学コースを参照してくださいlog n / log 2。あなたが選択したかどうかは関係ありませんlogか、log10この場合には、で割るlog新しいベースのトリックを行います。


0

Ustaman Sangatの改良版

static inline uint64_t
log2(uint64_t n)
{
    uint64_t val;
    for (val = 0; n > 1; val++, n >>= 1);

    return val;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.