2013-10-01 2 views
8

это один печатает 100:C: у меня разные результаты с pow (10,2) и pow (10, j), j = 2;

int j=2; 
int i= pow(10,2); 
printf("%d\n", i); 

и этот гравюр 99:

int j=2; 
int i= pow(10,j); 
printf("%d\n", i); 

почему?

+5

Какой компилятор вы используете и на какой системе? –

+1

Оба 100 для меня. –

+1

Мое предположение - это целочисленное усечение двойного результата, было бы интересно посмотреть, как выглядит ''% f ''. –

ответ

12

что происходит в том, что у вас есть реализация C, чья стандартная библиотека имеет очень низкую реализацию качества pow, который возвращает неточные результаты, даже если точный результат представлен в типе (double). Вызов pow(10,2), похоже, создает значение чуть ниже 100.0, которое при округлении до целого числа дает 99. Причина, по которой вы не видите это, когда аргументы постоянны, заключается в том, что компилятор взял на себя смелость оптимизировать вызов alltogether и замените его постоянным 100 в compiletime.

Если вы намерены выполнять целые полномочия, не используйте функцию pow. Напишите правильную целочисленную функцию мощности или когда показатель степени известен, просто напишите умножение напрямую.

+0

Да, даже если библиотека pow вычисляет точный результат, правильно округленный, полагаясь на каждое целое число, которое будет представлено точно в double, является хрупким. Я жду аванса 64 бит int, чтобы спросить, почему pow (11,18) неправильно вычисляется. –

+1

На практике это безопасно с этой точки зрения. «int» никогда не изменится в практических (не-DSP) реализациях из-за многих проблем, связанных с неожиданными эффектами целочисленного продвижения, проблемами исключения одного шага размера от стандартных типов и просто простой совместимостью. –

2

В первом случае я подозреваю, что компилятор оптимизировал значение до 10*10 без фактического вызова pow (составители действительно делают это). Во втором случае похоже, что у вас ошибка округления с плавающей запятой. В результате почти 100, но не совсем, и неявный литой до int усекает его.

Функция pow работает на double, а не int.

В целом (но не всегда), когда вы конвертируете двойные числа в целые числа, вы вызываете round(). Например:

int i = (int) round(pow(10,j)); 

Если ваша библиотека C не имеет этого, вы можете эмулировать:

#define round(x) floor((x)+0.5) 
+3

Почему это запрещено? – Adam

+0

маленькая опечатка: 'pow (10, 2) == 10 * 10' не' 10 << 1' – Adam

+0

О хе-хе, спасибо за это. Мой мозг был в другом месте. – paddy

0

pow возвращается double и поэтому, если результат не совсем 100, но немного меньше, чем это усечение, когда конвертируя в int, и вы получите результат 99, который вы видите. Было бы интересно посмотреть, как выглядят результаты для переменной double и %f.

Причина, по которой вы не увидите ее при использовании литералов, вызвана constant folding, что вызовет необходимость оптимизации и замену на pow. Мы можем видеть версию, которая использует константы, когда мы формируем сборку не вызов pow не сделал это просто перемещает конечный результат 100 (увидеть его live):

movl $100, -4(%rbp) 

в то время как версия второго варианта фактически вызывает pow (увидеть live):

call pow 
+0

Я получил 100 оба раза с двойным. –