Pytanie Dlaczego pow (n, 2) zwraca 24, gdy n = 5, z moim kompilatorem i systemem operacyjnym?


#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main()
{
    int n,i,ele;
    n=5;
    ele=pow(n,2);
    printf("%d",ele);
    return 0;
}

Wyjście to 24.

Używam GNU / GCC w Code :: Blocks.

Co się dzieje?

Wiem, że pow funkcja zwraca a double , ale 25 pasuje do typu int, więc dlaczego ten kod drukuje a 24 zamiast 25? Gdyby n=4; n=6; n=3; n=2; kod działa, ale z pięcioma nie działa.


21
2017-09-05 04:08


pochodzenie


Możesz spróbować przyjąć zwracaną wartość pow w float lub double zmienna, a następnie spróbuj ją typować int. Zobacz, czy to również produkuje 24 lub poprawna odpowiedź 25 - Don't You Worry Child
@exsnake - The pow funkcja nie robi jedynie pomnożenia 5 * 5. Prawdopodobnie jest to wynik końcowy 24.9999999 lub podobny wynik. The pow funkcja prawdopodobnie używa logarytmów do obliczenia wyniku, ponieważ musi również obsługiwać moce cząstkowe. Aby to potwierdzić, spójrz na implementację twojego kompilatora pow. - PaulMcKenzie
Powinieneś wyjaśnić, z jakiego systemu operacyjnego korzystasz, ponieważ prawie na pewno jest to błąd w implementacji części matematycznej standardowej biblioteki. Zgaduję, że używasz mingw z MSVCRT w systemie Windows ... - R..
Czy możesz udostępnić dane wyjściowe printf("%.25lf\n", pow(n,2)); na twojej implementacji gdzie n=5? - Mohit Jain
Dobry pow(n,2) zwróci dokładnie poprawne wyniki. C nie określa sposobu dobry  pow() musi być. - chux


Odpowiedzi:


Oto, co może się tutaj wydarzyć. Powinieneś być w stanie to potwierdzić, patrząc na implementację twojego kompilatora pow funkcjonować:

Zakładając, że masz poprawne # include, (wszystkie poprzednie odpowiedzi i komentarze na ten temat są poprawne - nie bierz #include pliki za pewnik), prototyp dla standardu pow funkcja jest następująca:

double pow(double, double);

i dzwonisz pow lubię to:

pow(5,2);

The pow funkcja przechodzi przez algorytm (prawdopodobnie za pomocą logarytmów), a zatem wykorzystuje funkcje zmiennoprzecinkowe i wartości do obliczenia wartości mocy.

The pow funkcja nie przechodzi przez naiwne "pomnóż wartość x łącznie n razy", ponieważ musi również obliczyć pow używając wykładników ułamkowych i nie można w ten sposób obliczyć mocy ułamkowych.

Więc więcej niż prawdopodobne, obliczenia pow użycie parametrów 5 i 2 spowodowało niewielki błąd zaokrąglenia. Po przypisaniu do intobetniesz wartość ułamkową, dając 24.

Jeśli używasz liczb całkowitych, możesz równie dobrze napisać własną "intpow" lub podobną funkcję, która po prostu mnoży wartość wymaganą liczbę razy. Korzyści z tego są następujące:

  1. Nie dostaniesz się do sytuacji, w której możesz uzyskać subtelne błędy zaokrąglania za pomocą pow.

  2. Twój intpow funkcja najprawdopodobniej działa szybciej niż równoważne wywołanie pow.


23
2017-09-05 04:42



Jeśli masz zamiar rzucić własną, całkowitą moc, prawdopodobnie powinieneś użyć potęgowanie przez kwadrowanie zamiast wielokrotnego mnożenia, ponieważ ten drugi jest Na) a ten pierwszy jest O (log n). - aruisdante
@aruisdante: Uwaga, notacja sugeruje przejście od liniowej do logarytmicznej złożoności, kiedy w rzeczywistości jest to przesunięcie z pseudo-liniowy do złożoności liniowej. - Ben Voigt
@BenVoigt Technicznie, poprzedni komentarz powinien być bardziej szczegółowy: algorytm naiwny wymaga operacji arytmetycznych O (n) (mnożenie lub dodawanie), gdzie n jest wykładnikiem; Squaring zmniejsza to do O (log (n)). Jeśli weźmiemy liczbę bitów w n jako rozmiar problemu, możemy również policzyć liczbę bitów w każdym produkcie i nie uważać mnożenia za operację o stałym czasie. W odniesieniu do praktycznych problemów osób wykonujących obliczenia numeryczne, myślę, że strona Wikipedii jest zbyt pedantyczna; w przypadku zagadnień o złożoności teoretycznej jego poprawność jest wątpliwa. - David K


Chcesz, aby wynik był wynikiem funkcji przeznaczonej dla debli.

Powinieneś chyba użyć

ele=(int)(0.5 + pow(n,2));
/*    ^    ^              */
/* casting and rounding   */

2
2017-09-05 05:06



Lepiej używać ele = round(0.5 + pow(n,2));. Chociaż w tym przypadku pow(n,2) nie powinien zwracać wyników mniejszych od zera, y = (int)(0.5 +x) to problem negatywny x. - chux
Nie lepiej! powinieneś po prostu użyć round(pow(n,2)). dodanie 0,5 przed zaokrągleniem skutecznie zaokrągla do następnej liczby całkowitej. Gdyby pow(n,2) zwrócił 25 plus epsilon, dostaniesz 26. - chqrlie


Jeśli nie #include <math.h> wtedy kompilator nie zna typów argumentów pow() które są jednymi i drugimi double nie int - więc otrzymasz niezdefiniowany wynik. Dostajesz 24, otrzymuję 16418.


1
2017-09-05 04:19



OP obejmuje math.h. nie ma szans na uzyskanie nieznacznego wyniku, nie uwzględniając math.h. więc my zupełnie inny problem. - Jean-François Fabre


Gdy użyjesz pow z zmiennymi, jego wynikiem jest double. Przypisywanie do int obcina to.

Możesz więc uniknąć tego błędu, przypisując wynik pow do double lub float zmienna.

Więc w zasadzie

Przekłada się na exp(log(x) * y) który da wynik, który nie jest dokładnie taki sam jak x^y - tylko przybliżenie w przybliżeniu jako wartość zmiennoprzecinkowa. Na przykład 5^2 stanie się 24.9999996 lub 25.00002


1
2017-09-05 04:31



zapytałem w teście do asystenta nauczyciela, który powiedział o użyciu natychmiastowego podwójnego testu i powiedział "tak", użyj int. Ale teraz widzę, że się mylił. - exsnake
Twój asystent nauczyciela musi zdać sobie sprawę, że nie powinieneś wywoływać funkcji, które są przeznaczone double typy i zakładają, że użyje integerna podstawie wdrożenia. - PaulMcKenzie


Arytmetyka zmiennoprzecinkowa nie jest dokładna.

Chociaż małe wartości można dodawać i odejmować dokładnie, to pow() Funkcja normalnie działa przez pomnożenie logarytmów, więc nawet jeśli dane wejściowe są dokładne, wynik nie jest prawidłowy. Przypisywanie do int zawsze obcina, więc jeśli niedokładność jest ujemna, otrzymasz 24 zamiast 25.

Morał tej historii polega na użyciu liczb całkowitych na liczbach całkowitych i podejrzenie <math.h> działa, gdy faktyczne argumenty mają być promowane lub skracane. Szkoda, że ​​GCC nie ostrzega, dopóki nie dodasz -Wfloat-conversion(nie ma go -Wall -Wextraprawdopodobnie dlatego, że jest wiele przypadków, w których taka konwersja jest przewidywana i pożądana).

W przypadku liczb całkowitych zawsze bezpieczniej i szybciej jest używać mnożenia (dzielenie, jeśli jest ujemne) niż pow() - zarezerwować to ostatnie tam, gdzie jest to potrzebne! Pamiętaj jednak o ryzyku przepełnienia.


1
2017-12-15 11:28