2

Данный код выводит ошибочный ответ, но только когда:

  • a = 3
  • используется переменная, а не константа

-

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

int main(){ int a = 3; int b = pow( 10, a -1 ); // 100 double c = pow( 10, a -1 ); // 100 int d = pow( 10, 2 ); // 100 int e = a * pow( 10, a-1 ); // 300 int f = a * (int)pow( 10, a-1 ); // 300

printf( "%d\n", b );                // выводит 99
printf( "%0.15f\n", c );            // выводит 100.000000000000000
printf( "%d\n", d );                // выводит 100
printf( "%d\n", e );                // выводит 299
printf( "%d\n", f );                // выводит 297

return 0; }

Почему это происходит?

Проверял на gcc 4.7.0 и g++ 4.7.0

tutankhamun
  • 11,366

2 Answers2

5

@purple-Sketch, Спасибо за отличный вопрос !

Похоже на то, что результат pow() (вычисления в double) чуть меньше 100, а printf ("%0.15f) округляет результат (по крайней мере в MinGW 32-bit на Windows 64-bit (привет Б. Гейтсу?)).

При преобразовании double -> int дробная часть отбрасывается.

При вычислении int d = pow( 10, 2 ); компилятор все вычисляет сам (в ассемблерном коде нет иструкции call _pow)

При вычислении int e = a * pow( 10, a-1 ); a перед умножением преобразуется в double, поэтому результат 299.

Windows 7

c:/Users/avp/src/cc/hashcode $ gcc powerr.c 
c:/Users/avp/src/cc/hashcode $ ./a
99
100.000000000000000
100
299
297
c:/Users/avp/src/cc/hashcode $ gcc --version
gcc.exe (GCC) 3.4.5 (mingw-vista special r3)

Ubuntu

avp@avp-ubu1:~/hashcode$ gcc powerr.c -lm
avp@avp-ubu1:~/hashcode$ ./a.out 
100
100.000000000000000
100
300
300
avp@avp-ubu1:~/hashcode$ cat /etc/issue
Ubuntu 10.04.4 LTS \n \l

avp@avp-ubu1:~/hashcode$ gcc --version gcc.real (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3

Ubuntu стоит на 64-bit машине в 64-bit ОС.

avp
  • 46,098
  • 6
  • 48
  • 116
  • но ведь double, если я не ошибаюсь, можеть содержать до 15 знаков после запятой. Если выводить их все, почему происходит округление? – purple-Sketch Aug 18 '12 at 14:38
  • 1
    Добавил проверочку
        double delta = pow(10,a-1) - 100.0;
        printf ("delta = %e %s\n",delta, delta == 0? "eq":"ne");
    
    

    получил

    delta = -6.938894e-018 ne
    
    

    Вот и ответ. Так в M$ видят pow().

    – avp Aug 18 '12 at 16:10
  • @purple-Sketch Слово "округление" Вы использовали неправильно. В C при выполнении
    int b = pow( 10, a -1 );
    
    

    применяется не округление (round), а отбрасывание дробной части. А поскольку pow(a, b) в общем случае это exp(log(a)*b), то результат вполне закономерен и предсказуем. Случай

    int d = pow( 10, 2 );
    
    

    как отметил @avp -- проделки оптимизатора.

    – alexlz Aug 19 '12 at 04:12
-1

Потому что приоритет у операции возведения в степень. Да и не стоит делать вычисления в на месте аргументов функции, предполагаю что сначала возводиться в степень 2 (а-1) а потом еще делается минус 1

а какая реакция если вместо а=3 будет а=5 ?

Rsecomua
  • 4,263
  • при любом значении a кроме 3, при использовании константы ( как в 'd' ) работает как и ожидалось.

    приоритет думаю тут роли не играет. в выражении ( a * pow( 10, a-1) ) pow вычисляется только раз.

    – purple-Sketch Aug 18 '12 at 03:57
  • 2
    pow - это не оператор, а функция. А делать вычисления и т.п. в аргументах при вызове функции - нормальная практика. – skegg Aug 18 '12 at 12:11