2012-03-11 30 views
7

Tôi đã đọc qua How can I write a power function myself? và câu trả lời được đưa ra bởi dan04 hút sự chú ý của tôi chủ yếu là bởi vì tôi không chắc chắn về câu trả lời được đưa ra bởi fortran, nhưng tôi mất đó và thực hiện điều này:tự làm pow() C++

#include <iostream> 
using namespace std; 
float pow(float base, float ex){ 
    // power of 0 
    if (ex == 0){ 
     return 1; 
    // negative exponenet 
    }else if(ex < 0){ 
     return 1/pow(base, -ex); 
    // even exponenet 
    }else if ((int)ex % 2 == 0){ 
     float half_pow = pow(base, ex/2); 
     return half_pow * half_pow; 
    //integer exponenet 
    }else{ 
     return base * pow(base, ex - 1); 
    } 
} 
int main(){ 
    for (int ii = 0; ii< 10; ii++){\ 
     cout << "pow(" << ii << ".5) = " << pow(ii, .5) << endl; 
     cout << "pow(" << ii << ",2) = " << pow(ii, 2) << endl; 
     cout << "pow(" << ii << ",3) = " << pow(ii, 3) << endl; 
    } 
} 

mặc dù tôi không chắc chắn liệu tôi có dịch được quyền này hay không vì tất cả các cuộc gọi đều trả về .5 khi số mũ trả về 0. Trong câu trả lời, nó có thể cần log2 (x) dựa trên a^b = 2^(b * log2(a)), nhưng tôi không chắc chắn trong khi tôi không chắc chắn nơi để đặt nó, hoặc nếu tôi thậm chí còn suy nghĩ về quyền này.

LƯU Ý: Tôi biết rằng điều này có thể được xác định trong thư viện toán học, nhưng tôi không cần tất cả các chi phí bổ sung của toàn bộ thư viện toán cho một vài chức năng.

EDIT: có ai biết thực hiện dấu phẩy động cho số mũ phân số không? (Tôi đã thấy một triển khai kép, nhưng đó là sử dụng một thủ thuật với thanh ghi, và tôi cần dấu phẩy động, và thêm một thư viện chỉ để làm một thủ thuật tôi sẽ tốt hơn là chỉ cần bao gồm thư viện toán học)

+3

Hoặc các chi phí gia tăng của một FPU? –

+2

bạn thiếu số mũ phân số (mã có số mũ bằng nhau) - nhìn vào liên kết gốc, tôi nghĩ bạn đang sao chép từ thứ chỉ hỗ trợ số nguyên nguyên (do đó kiểm tra phân số sẽ thất bại). –

+0

@andrewcooke dang nó. bạn có thể giúp tôi bằng cách hiển thị những thay đổi cần thiết – gardian06

Trả lời

14

Dưới đây là các liên kết đến việc triển khai thực sự của powf.

Tôi hy vọng các giải pháp đơn giản sẽ thiếu độ chính xác trong kết quả hoặc không xử lý các tham số InF và NaN.

http://opensource.apple.com/source/Libm/Libm-2026/Source/Intel/expf_logf_powf.c

http://opensource.apple.com/source/Libm/Libm-315/Source/ARM/powf.c

+0

+1 không tầm thường để thực hiện pow. Một vấn đề, rằng Newton-Raphson cho máy tính n'th gốc hội tụ rất chậm (* không * rất nhanh) cho n và x cao. Việc thực hiện câu trả lời này liên kết đến, dường như chỉ có nghĩa là 'float', tránh điều đó. –

+0

Đây là đá quý đã được tìm kiếm trong 3 tuần qua !! Cảm ơn đã đăng bài viết! – nimig18

2

Tôi nghĩ thuật toán bạn đang tìm kiếm có thể là 'nth root'. Với một đoán ban đầu trong tổng số 1 (cho k == 0):

#include <iostream> 
using namespace std; 


float pow(float base, float ex); 

float nth_root(float A, int n) { 
    const int K = 6; 
    float x[K] = {1}; 
    for (int k = 0; k < K - 1; k++) 
     x[k + 1] = (1.0/n) * ((n - 1) * x[k] + A/pow(x[k], n - 1)); 
    return x[K-1]; 
} 

float pow(float base, float ex){ 
    if (base == 0) 
     return 0; 
    // power of 0 
    if (ex == 0){ 
     return 1; 
    // negative exponenet 
    }else if(ex < 0){ 
     return 1/pow(base, -ex); 
    // fractional exponent 
    }else if (ex > 0 && ex < 1){ 
     return nth_root(base, 1/ex); 
    }else if ((int)ex % 2 == 0){ 
     float half_pow = pow(base, ex/2); 
     return half_pow * half_pow; 
    //integer exponenet 
    }else{ 
     return base * pow(base, ex - 1); 
    } 
} 
int main_pow(int, char **){ 
    for (int ii = 0; ii< 10; ii++){\ 
     cout << "pow(" << ii << ", .5) = " << pow(ii, .5) << endl; 
     cout << "pow(" << ii << ", 2) = " << pow(ii, 2) << endl; 
     cout << "pow(" << ii << ", 3) = " << pow(ii, 3) << endl; 
    } 
    return 0; 
} 

kiểm tra:

pow(0, .5) = 0.03125 
pow(0, 2) = 0 
pow(0, 3) = 0 
pow(1, .5) = 1 
pow(1, 2) = 1 
pow(1, 3) = 1 
pow(2, .5) = 1.41421 
pow(2, 2) = 4 
pow(2, 3) = 8 
pow(3, .5) = 1.73205 
pow(3, 2) = 9 
pow(3, 3) = 27 
pow(4, .5) = 2 
pow(4, 2) = 16 
pow(4, 3) = 64 
pow(5, .5) = 2.23607 
pow(5, 2) = 25 
pow(5, 3) = 125 
pow(6, .5) = 2.44949 
pow(6, 2) = 36 
pow(6, 3) = 216 
pow(7, .5) = 2.64575 
pow(7, 2) = 49 
pow(7, 3) = 343 
pow(8, .5) = 2.82843 
pow(8, 2) = 64 
pow(8, 3) = 512 
pow(9, .5) = 3 
pow(9, 2) = 81 
pow(9, 3) = 729 
+0

xin lỗi đã có các dự án khác để thực hiện. sự cố này xảy ra khi được cho là '.3' – gardian06

+0

Tôi đã thử thêm .3, dường như hoạt động (tôi đang sử dụng GCC 4.5). nhưng hãy xem bản chỉnh sửa cho * base * == 0 ... – CapelliC

+0

Tôi đã thử thêm 1/3 số mũ, giá trị bị cắt xén, tôi sẽ kiểm tra thêm ... – CapelliC

6

Tôi đã nhìn vào tờ giấy này here trong đó mô tả làm thế nào để xấp xỉ hàm mũ cho độ chính xác kép. Sau một ít nghiên cứu về Wikipedia về biểu diễn điểm nổi chính xác đơn, tôi đã tìm ra các thuật toán tương đương. Họ chỉ thực hiện chức năng exp, vì vậy tôi tìm thấy một chức năng nghịch đảo cho các bản ghi và sau đó chỉ cần làm

POW(a, b) = EXP(LOG(a) * b). 

biên soạn gcc4.6.2 này mang lại một hàm pow nhanh hơn so với thực hiện các thư viện chuẩn của gần 4 lần (biên soạn với O2) .

Lưu ý: mã cho EXP được sao chép gần như nguyên văn từ giấy tôi đã đọc và chức năng LOG được sao chép từ here.

Đây là mã có liên quan:

#define EXP_A 184 
    #define EXP_C 16249 

    float EXP(float y) 
    { 
     union 
     { 
     float d; 
     struct 
     { 
    #ifdef LITTLE_ENDIAN 
      short j, i; 
    #else 
      short i, j; 
    #endif 
     } n; 
     } eco; 
     eco.n.i = EXP_A*(y) + (EXP_C); 
     eco.n.j = 0; 
     return eco.d; 
    } 

    float LOG(float y) 
    { 
     int * nTemp = (int*)&y; 
     y = (*nTemp) >> 16; 
     return (y - EXP_C)/EXP_A; 
    } 

    float POW(float b, float p) 
    { 
     return EXP(LOG(b) * p); 
    } 

Hiện vẫn còn một số tối ưu hóa bạn có thể làm ở đây, hoặc có lẽ đó là đủ tốt. Đây là một xấp xỉ thô nhưng nếu bạn đã hài lòng với các lỗi được giới thiệu bằng cách sử dụng biểu diễn kép, tôi tưởng tượng điều này sẽ đạt yêu cầu.

3

Tôi nghĩ rằng bạn có thể thử giải quyết nó bằng cách sử dụng chuỗi Taylor, kiểm tra điều này. http://en.wikipedia.org/wiki/Taylor_series

Với chuỗi Taylor, bạn có thể giải quyết mọi khó khăn để giải toán tính toán như 3^3.8 bằng cách sử dụng các kết quả đã biết như 3^4. Trong trường hợp này, bạn có 3^4 = 81 để

3^3.8 = 81 + 3.8 * 3 (3.8 - 4) + .. + .. và cứ thế phụ thuộc vào độ lớn của bạn, bạn sẽ nhận được giải pháp gần hơn về vấn đề của bạn.

+0

Taylor loạt đến một câu hỏi của đường cong phù hợp, và đòi hỏi phải biết một số thích hợp của các điều khoản để sử dụng để tạo ra một giá trị chính xác. đến vài và bạn có thể dễ dàng đạt được sự gia tăng/giảm liên tục, và nhiều, và các quá trình lãng phí của bạn. có một gợi ý hợp lý về số k của tổng kết không? – gardian06

+0

Bạn có thể so sánh kết quả của phép tính cuối cùng trong chuỗi với một số số tiêu chuẩn như 0.0000001 vì vậy đây là giới hạn về độ chính xác. Vì vậy, bạn phải làm cho nó như là iteration như một tổng và kiểm tra tính toán của mỗi iteration nếu giá trị cuối cùng của nó là AlexTheo

+0

Chuỗi Taylor chỉ xấp xỉ tại một điểm duy nhất mà khi thuật toán minimax như (chebyshev, remez) vượt quá giới hạn, cho phép lỗi equi-gipple tối ưu. – nimig18

1

Tôi và bạn tôi phải đối mặt với vấn đề tương tự khi chúng tôi đang ở trong một dự án OpenGL và math.h không đủ trong một số trường hợp.Người hướng dẫn của chúng tôi cũng có cùng một vấn đề và anh ấy nói với chúng tôi để phân chia quyền lực cho các số nguyên và các phần nổi. Ví dụ: nếu bạn tính toán x^11.5, bạn có thể tính toán sqrt (x^115, 10), điều này có thể dẫn đến kết quả chính xác hơn.

+0

điều này sẽ yêu cầu một số mũ lớn hơn để bắt đầu. mặc dù nó có thể đơn giản hóa việc tính lũy thừa điểm, nó cũng sẽ tăng số lượng đệ quy, hoặc lặp lại các cuộc gọi được thực hiện bên trong chức năng nguồn (xem xét trừ khi toán bit đang được thực hiện nó cần một số loại vòng lặp) – gardian06

Các vấn đề liên quan