2017-06-15 71 views
6

Potrzebuję wersji pow dla liczb całkowitych. Mam 2 problemy, które trzeba rozwiązać z pow:pow dla wartości całkowych

  1. Jeśli wynik jest większy niż mój integralną typu muszę zacisnąć do numeric_limits::max()
  2. muszę być w stanie radzić sobie z 41.99999 zaokrąglając w górę do 42, zamiast w dół do 41

robi C++ podać mi jakieś rozwiązanie inline tutaj, czy jestem zatrzymany pisząc własną funkcję:

template <typename T> 
enable_if_t<is_integral_v<T>, T> mypow(const T base, unsigned int exp) { 
    T result = exp == 0U ? base : 1; 

    while(exp-- > 1U) { 
     if(numeric_limits<T>::max()/result <= base) return numeric_limits<T>::max(); 
     result *= base; 
    } 
    return result; 
} 
+0

Nie możesz użyć ['std :: pow'] (http://en.cppreference.com/w/cpp/numeric/math/pow) i skrócić wynik, jeśli jest to konieczne? – NathanOliver

+0

@NathanOliver Tak ... jeśli był sposób na zagwarantowanie ** 1 ** i ** 2 ** nie zostałby naruszony przez to. Czy istnieje sposób, aby to osiągnąć? –

+0

Można użyć rekursji ogonowej i niektórych sztuczek numerycznych w celu poprawy wydajności środowiska wykonawczego. Co więcej, możesz uczynić tę metodę constexpr. https://stackoverflow.com/questions/9348933/using-recursion-to-raise-a-base-to-its-exponent-c – Andrew

Odpowiedz

3

Czy C++ podać mi jakieś rozwiązanie tutaj

inline Nie, nie ma całkowitą pow w bibliotece standardowej.

czy jestem zatrzymany pisać własne funkcje

Tak, masz napisać własną funkcję. Należy pamiętać, że przedstawione pętla mnożenia może być wolniejsze niż przy użyciu std::pow do realizacji funkcji, zwłaszcza, że ​​masz również oddział i podział w pętli:

template<class I> 
I int_pow_no_overflow(I base, I exp) 
{ 
    double max = std::numeric_limits<I>::max(); 
    double result = std::round(std::pow(base, exp)); 
    return result >= max 
     ? max 
     : result; 
} 

na bardziej ogólne podejście, może warto rozważyć niedomiar jako dobrze.

Istnieją także inne, szybsze algorytmy (patrz na przykład algorytm szybkiego potęgowania) dla całkowitej potęgowania niż jednego liniowego pokazałeś, ale jestem pewien, czy nie byłoby warto rozważyć je chyba że mamy do czynienia z dowolną dokładnością arytmetyczna lub wbudowany system bez jednostki zmiennoprzecinkowej.

+0

Jeśli masz lepszy sposób na uniknięcie obcinania i zaciskania za pomocą 'pow' I wszystkie uszy. To, co mam, było najlepsze, jakie mogłem wymyślić, i jest bardzo drogie. –

+0

Dlaczego potrzebujesz ogólnego programowania? –

+1

@ JonatonMee jako wskazówka, N^8 = N^4 * N^4, więc nie musisz dwukrotnie obliczać N^4. –

2

Twój kod nie został skompilowany, powinieneś, jeśli to możliwe, najpierw sprawdzić kompilację kodu, użyć kompilatora lub najpierw sprawdzić go na kompilatorze.

Ponadto zapomniałeś wziąć pod uwagę wartości ujemne. To bardzo ważna cecha mocy integralnych. Poniższy kod dotyczy zwykłego typu int. Pozwolę ci zbadać, jak możesz rozszerzyć go na inne integralne typy.

#include <type_traits> 
#include <iostream> 
#include <cmath> 
#include <limits> 

using namespace std; 

template <typename T> 
enable_if_t< is_integral<T>::value, T> 
mypow(T base, unsigned int exp) 
{ 
    T result = T(1); 
    bool sign = (base < 0); 

    if (sign) base = -base; 

    T temp = result; 
    while(exp-- != 0) 
    { 
     temp *= base; 
     if (temp < result) 
     { 
      return (sign) ? numeric_limits<T>::min() 
          : numeric_limits<T>::max(); 
     } 
     result = temp; 
    } 
    return (sign && (exp & 1)) ? -result : result; 
} 

template <typename T> 
enable_if_t< !is_integral<T>::value, int> 
mypow(const T& base, unsigned int exp) 
{ 
    T result = T(1); 
    int i_base = int(floor(base + .5)); 
    bool sign = (i_base < 0); 

    if (sign) i_base = -i_base; 

    int temp = result; 
    while(exp-- != 0) 
    { 
     temp *= i_base; 
     if (temp < result) 
     { 
      return (sign) ? numeric_limits<int>::min() : numeric_limits<int>::max(); 
     } 
     result = temp; 
    } 
    return (sign && (exp & 1)) ? -result : result; 
} 

W prawdziwym życiu, zrobiłbym tę notatkę użycie podłogi, nawet w integralnym przypadku.

template<typename T> 
    enable_if_t< is_integral<T>::value, T> 
    mypow(T x, unsigned int y) { return T(floor(pow(x, y) + .5)); } 

    template<typename T> 
    enable_if_t< !is_integral<T>::value, int> 
    mypow(T x, unsigned int y) { return int(floor(pow(floor(x + .5), y) + .5)); } 
+0

Chciałem tego tylko jako przykładu, ale tak, jest to kolejny powód, dla którego nie chcę pisać z własną funkcją :( –

+0

Używanie zmiennoprzecinkowego pow będzie generalnie szybsze w procesorze obsługującym zmiennoprzecinkowe operacje ops. współczesne procesory mają instrukcję zmiennoprzecinkową o pow. –

+1

@ JonathanMee: Może powinieneś sprawdzić tę interesującą dyskusję na ten temat: https://stackoverflow.com/questions/101439/the-most-efficient-way-to-implementan-an -integer-based-power-function-powint-int –