2015-10-19 4 views
3

Мне интересно, где происходит числовая ошибка, на каком уровне. Поясню на примере:pow numeric error in c

int p = pow(5, 3); 
printf("%d", p); 

Я тестировал этот код на различных HW и компиляторов (VS и GCC), а некоторые из них распечатать 124, а некоторые из 125.

  • На то же HW (OS) я получаю разные результаты в разных компиляторах (VS и GCC).
  • На разных HW (OS) я получаю разные результаты в одном и том же компиляторе (cc (GCC) 4.8.1).

AFAIK, pow вычисляет до 124.99999999, и это усекается до int, но где эта ошибка? Или, другими словами, где происходит коррекция (124.99-> 125)

Является ли это компилятором-HW-взаимодействие?

// ****** отредактирован:

Вот дополнительный фрагмент, чтобы играть с (держать глаз на р = 5, р = 18, ...):

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

int main(void) { 
    int p; 
    for (p = 1; p < 20; p++) { 
     printf("\n%d %d %f %f", (int) pow(p, 3), (int) exp(3 * log(p)), pow(p, 3), exp(3 * log(p))); 
    } 
    return 0; 
} 
+2

Значения с плавающей точкой на компьютерах являются сложным делом, а результаты операций над ними зависят от многих факторов. Например, поддержка аппаратного обеспечения (если таковая имеется), алгоритмы округления, используемые различным программным обеспечением (компиляторы, стандартные библиотеки) или аппаратное обеспечение, и многое другое. –

+1

Также следует помнить, что при использовании константных выражений компилятор часто будет использовать [встроенные функции] (http://stackoverflow.com/a/24294632/1708801), и поэтому вы также можете попробовать это с помощью '-fno-builtin' для посмотрите, влияет ли это на результаты. –

+0

Я считаю, что это не так, я только что добавил фрагмент, используя цикл for, и то же самое происходит. – igorludi

ответ

2

(Прежде всего обратите внимание, что для типа с плавающей точкой двойной точности IEEE754 все целые числа могут быть представлены целыми числами до 53-й степени 2. Точность погрешности с плавающей запятой для целых pow неточностей неверна).

pow(x, y) обычно используется в C как exp(y * log(x)). Следовательно, он может «уйти» даже на довольно мелкие интегральные случаи.

Для небольших интегральных случаев я обычно записываю вычисления долгое время, а для других интегральных аргументов я использую стороннюю библиотеку. Хотя самодостаточное решение с использованием цикла for является заманчивым, существуют эффективные оптимизации, которые могут быть сделаны для интегральных мощностей, которые такое решение не может использовать.

Что касается наблюдаемых различных результатов, то мог бы спуститься до некоторых платформ, используя 80-битный посредник с плавающей запятой. Возможно, некоторые из вычислений выше 125, а другие - ниже.

+0

ОК, я понимаю это, и я знаю, что это не лучшая практика (pow-> int). Я также получаю, что pow создает ошибки округления, но я не понимаю, где эта ошибка возникает, как я сказал в вопросе. – igorludi

+2

Что касается тех, кто ищет эффективную реализацию int pow: http://stackoverflow.com/questions/101439/the-most-efficient-way-to-implement-an-integer-based-power-function-powint-int – igorludi

+1

' log (5) 'является' double', который ** не может быть представлен точно. 'exp (3 log (5))' будет, скорее всего, ** не ** быть целым числом. – Bathsheba