C / C ++에서 log base (2)를 작성하는 방법


99

log (base 2) 함수를 작성하는 방법이 있습니까?

C 언어에는 2 개의 내장 기능이 있습니다 .-- >>

1. log베이스 e.

2. log10베이스 10;

하지만 2 진법의 로그 함수가 필요합니다.


1
안구 계산의 경우 밑이 2 인 로그는 밑이 10 인 로그에 자연 로그를 더한 값과 거의 같습니다. 분명히 프로그램에서 더 정확하고 빠른 버전을 작성하는 것이 좋습니다.
David Thornley 2010-06-17

정수의 경우, 루프 카운트가 로그의 근사치 0에 도달 할 권리 bitshift에 루프 및 정지 할 수
실레 Starynkevitch

답변:


199

간단한 수학 :

    로그 2 ( x ) = log y ( x ) / log y (2)

여기서 y 는 무엇이든 가능하며 표준 로그 함수의 경우 10 또는 e 입니다.



53

적분 결과를 찾고 있다면 값에 설정된 가장 높은 비트를 결정하고 그 위치를 반환 할 수 있습니다.


27
이에 대한 멋진 비트 트위들 링 방법도 있습니다 (Java의 Integer.highestOneBit(int)방법 i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); i |= (i >> 8); i |= (i >> 16); return i - (i >>> 1);
Joey

38
... 나while (i >>= 1) { ++l; }
리 다니엘 크로커

2
@Joey 그것은 정수가 32 비트 너비라고 가정하면 작동합니다. 64 비트의 경우 추가 i>>32. 그러나 Java에는 32 비트 정수만 있으므로 괜찮습니다. C / C ++의 경우 고려할 필요가 있습니다.
Zoso

43
#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

@Johannes : 컴파일러가 log10 (2)을 미리 계산할 것 같지 않습니다. 컴파일러는 log10이 매번 같은 값을 반환한다는 것을 모릅니다. 컴파일러가 알고있는 모든 것에 대해 log10 (2)는 연속 호출에서 다른 값을 반환 할 수 있습니다.
abelenky

@abelenky : 좋아, 나는 그것을 되 찾는다. 컴파일러는 log()구현에 대한 소스를 볼 수 없으므로 수행하지 않습니다. 내 잘못이야.
Joey

3
@abelenky : log10()C 표준에 정의 된 함수 이므로 컴파일러는 결과를 미리 계산하는 것을 포함하여 "특별히"처리 할 수 ​​있습니다. 제가 @Johannes의 제안이라고 생각합니다.
caf

1
@CarlNorum : 방금 확인했는데 gcc 4.7은 적어도 log10(2)상수로 대체 됩니다.
caf

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의 다음 전원을 내림에 의해 바로 이동할 수
Safayet 아메드

@SafayetAhmed 그 방법으로 숫자의 log2를 찾는 방법을 설명하십시오. 그 가치를 얻는 더 쉬운 방법을 모르겠습니다. 조회 테이블과 함께 위의 산술을 사용하는 것 외에도 반복 / 재귀 알고리즘을 사용하거나 전용 / 내장 하드웨어를 사용하여 계산을 수행 할 수 있습니다.
bkausbk

32 비트 변수 v의 비트에 0 (LSB)에서 N (MSB)까지 번호가 지정되어 있다고 가정합니다. v의 최상위 세트 비트가 n이라고 가정합니다. n이 floor (log2 (v))를 나타낸다고 말하는 것이 맞습니까? n 주어진 v를 찾는 데 관심이 없습니까?
Safayet Ahmed

내가 설명한 것이 실제 로그가 아니라 가장 가까운 2의 제곱을 제공한다는 것을 깨달았습니다. 곱셈 및 테이블 조회는 2의 거듭 제곱에서 로그로 이동하기위한 것입니다. 숫자 0x07C4ACDD를 어느 정도 왼쪽으로 이동하고 있습니다. 왼쪽으로 이동하는 양은 2의 거듭 제곱에 따라 달라집니다. 숫자는 연속 된 5 비트 시퀀스가 ​​고유 한 것입니다. (0000 0111 1100 0100 0110 1100 1101 1101)은 (00000) (00001) ... (11101) 시퀀스를 제공합니다. 얼마나 왼쪽으로 이동했는지에 따라 이러한 5 비트 패턴 중 하나를 얻게됩니다. 그런 다음 테이블 조회. 아주 좋아.
Safayet Ahmed

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

단순성, 명확성 및 OP의 주어진 정보를 기반으로 코드 제공을 위해 찬성 투표하십시오.
yoyo

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 케이스를 확인했다고 가정합니다. 다른 문제는 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를 계산해야했습니다.

온도 = (((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.