Cách viết log base (2) trong c / c ++


98

Có cách nào để viết hàm log (cơ số 2) không?

Ngôn ngữ C có 2 chức năng tích hợp - >>

1. lognào là bazơ e.

2. log10căn 10;

Nhưng tôi cần hàm log của cơ số 2. Làm thế nào để tính toán điều này.


1
Đối với phép tính nhãn cầu, logarit cơ số 2 gần bằng logarit cơ số 10 cộng với logarit tự nhiên. Rõ ràng là tốt hơn nên viết một phiên bản chính xác hơn (và nhanh hơn) trong một chương trình.
David Thornley

Đối với số nguyên, bạn có thể lặp trên bitshift đúng, và dừng lại khi đạt 0. Số đếm vòng lặp là một xấp xỉ của các bản ghi
Basile Starynkevitch

Câu trả lời:


198

Toán học đơn giản:

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

trong đó y có thể là bất kỳ thứ gì, đối với các hàm log tiêu chuẩn là 10 hoặc e .



53

Nếu bạn đang tìm kiếm một kết quả tích phân, bạn chỉ có thể xác định bit cao nhất được đặt trong giá trị và trả về vị trí của nó.


27
Ngoài ra còn có một phương pháp bit twiddling đẹp cho điều này (lấy từ Java của Integer.highestOneBit(int)phương pháp):i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); i |= (i >> 8); i |= (i >> 16); return i - (i >>> 1);
Joey

38
... hoặcwhile (i >>= 1) { ++l; }
Lee Daniel Crocker

2
@Joey Điều đó sẽ hoạt động giả sử số nguyên là 32 bit, không? Đối với 64 bit, nó sẽ có thêm i>>32. Nhưng vì Java chỉ có ints 32-bit, nên nó ổn. Đối với C / C ++, nó cần được xem xét.
Zoso

43
#define M_LOG2E 1.44269504088896340736 // log2(e)

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

(phép nhân có thể nhanh hơn phép chia)


1
Chỉ muốn làm rõ - sử dụng các quy tắc chuyển đổi nhật ký + thực tế là log_2 (e) = 1 / log_e (2) -> chúng tôi nhận được kết quả
Guy L


9

Như đã nêu trên http://en.wikipedia.org/wiki/Logarithm :

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

Có nghĩa là:

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

11
Lưu ý rằng bạn có thể tính toán trước log10 (2) để tăng hiệu suất.
corsiKa

@Johannes: Tôi nghi ngờ trình biên dịch sẽ tính toán trước log10 (2). Trình biên dịch không biết rằng log10 sẽ trả về cùng một giá trị mọi lúc. Đối với tất cả những gì trình biên dịch biết, log10 (2) có thể trả về các giá trị khác nhau trong các lần gọi liên tiếp.
abelenky

@abelenky: Ok, tôi rút lại. Vì trình biên dịch không bao giờ nhìn thấy nguồn để log()triển khai nên nó sẽ không làm điều đó. Lỗi của tôi.
Joey

3
@abelenky: Vì log10()một chức năng được định nghĩa trong tiêu chuẩn C, trình biên dịch có thể tự do xử lý nó "đặc biệt", bao gồm tính toán trước kết quả, mà tôi tin rằng đó là gợi ý của @Johannes?
caf

1
@CarlNorum: Tôi vừa kiểm tra và gcc 4.7 ít nhất thay thế log10(2)bằng một hằng số.
caf

8

Nếu bạn muốn làm cho nó nhanh, bạn có thể sử dụng một bảng tra cứu như trong Bit Twiddling Hacks (chỉ số nguyên 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];

Ngoài ra, bạn nên xem xét các phương thức tích hợp trình biên dịch của mình như phương thức _BitScanReversenào thể nhanh hơn vì nó có thể hoàn toàn được tính toán trong phần cứng.

Cũng hãy xem có thể trùng lặp Làm thế nào để thực hiện một số nguyên log2 () trong C ++?


Tại sao phép nhân và bảng tra cứu ở cuối? Bạn không thể chỉ làm (v + 1) mà sẽ làm tròn sức mạnh tiếp theo của hai? Và sau đó, bạn có thể chuyển sang phải một để làm tròn xuống mức tiếp theo là 2.
Safayet Ahmed

@SafayetAhmed Vui lòng mô tả cách bạn muốn tìm log2 của một số bằng phương pháp đó. Tôi không biết cách dễ dàng hơn để có được giá trị đó. Bên cạnh việc sử dụng số học ở trên với bảng tra cứu, người ta có thể sử dụng thuật toán lặp / đệ quy hoặc sử dụng phần cứng chuyên dụng / nội trang để tính toán.
bkausbk

Giả sử các bit của một biến 32 bit v được đánh số từ 0 (LSB) đến N (MSB). Giả sử tập bit quan trọng nhất của v là n. Có đúng không khi nói rằng n đại diện cho tầng (log2 (v))? Bạn không quan tâm đến việc chỉ tìm n cho trước v?
Safayet Ahmed

Tôi nhận ra rằng những gì tôi mô tả sẽ chỉ cung cấp cho bạn lũy thừa thấp nhất gần nhất của 2 chứ không phải lôgarit thực tế. Phép nhân và tra cứu bảng là để đi từ lũy thừa của hai đến lôgarit. Bạn đang chuyển số 0x07C4ACDD sang trái một số. Số tiền bạn dịch chuyển sang trái sẽ phụ thuộc vào sức mạnh của hai. Con số là như vậy, mà bất kỳ chuỗi 5 bit liên tiếp nào là duy nhất. (0000 0111 1100 0100 0110 1100 1101 1101) cung cấp cho bạn các chuỗi (00000) (00001) ... (11101). Tùy thuộc vào việc bạn dịch chuyển sang trái bao xa, bạn sẽ nhận được một trong các mẫu 5 bit này. Sau đó tra cứu bảng. Rất đẹp.
Safayet Ahmed

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

Ủng hộ vì sự đơn giản, rõ ràng và cung cấp mã dựa trên thông tin OP đưa ra.
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;
 }

Về cơ bản giống như tomlogic của.


1
Có một vài điều sai với giải pháp này, nhưng nói chung, điều này là tốt nếu bạn muốn tránh các điểm nổi. Bạn đang dựa vào tràn để điều này hoạt động vì bạn đang khởi tạo một số nguyên không dấu với -1. Điều này có thể được khắc phục bằng cách khởi tạo nó thành 0 và sau đó trả về giá trị - 1, giả sử bạn kiểm tra trường hợp 0. Vấn đề khác là bạn đang dựa vào vòng lặp dừng khi n == 0, bạn nên nêu rõ ràng. Ngoài ra, điều này rất tốt nếu bạn muốn tránh các dấu chấm động.
Rian Quinn

2

Bạn phải bao gồm math.h (C) hoặc cmath (C ++) Tất nhiên, hãy nhớ rằng bạn phải tuân theo các phép toán mà chúng ta biết ... chỉ các số> 0.

Thí dụ:

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

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

2

Tôi cần có độ chính xác cao hơn rằng vị trí của bit quan trọng nhất và bộ vi điều khiển tôi đang sử dụng không có thư viện toán học. Tôi thấy rằng chỉ cần sử dụng xấp xỉ tuyến tính giữa 2 ^ n giá trị cho các đối số giá trị nguyên dương là hoạt động tốt. Đây là mã:

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));
}

Trong chương trình chính của mình, tôi cần tính N * log2 (N) / 2 với kết quả là số nguyên:

temp = (((uint32_t) N) * khoảng_log_base_2_N_times_256) / 512;

và tất cả các giá trị 16 bit không bao giờ bị tắt quá 2%


1

Tất cả các câu trả lời trên đều đúng. Câu trả lời này của tôi dưới đây có thể hữu ích nếu ai đó cần nó. Tôi đã thấy yêu cầu này trong nhiều câu hỏi mà chúng tôi đang giải quyết bằng cách sử dụng C.

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

Tuy nhiên, nếu bạn đang sử dụng ngôn ngữ C và bạn muốn kết quả là số nguyên, bạn có thể sử dụng như sau:

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

Hi vọng điêu nay co ich.


0

Tham khảo khóa học toán học cơ bản của bạn , log n / log 2. Không quan trọng là bạn chọn loghay log10trong trường hợp này, chia logcho cơ sở mới là một mẹo.


0

Phiên bản cải tiến của những gì Ustaman Sangat đã làm

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

    return val;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.