Я использую некоторые макросы и наблюдаю за некоторым странным поведением.
Я определил PI как константу, а затем использовал ее в макросах для преобразования градусов в радианы и радианов в градусы. Градусы в радианах работают нормально, а в радианах в градусах нет:
piTest.cpp:
#include <cmath>
#include <iostream>
using namespace std;
#define PI atan(1) * 4
#define radians(deg) deg * PI / 180
#define degrees(rad) rad * 180 / PI
int main()
{
cout << "pi: " << PI << endl;
cout << "PI, in degrees: " << degrees(PI) << endl;
cout << "45 degrees, in rad: " << radians(45) << endl;
cout << "PI * 180 / PI: " << (PI * 180 / PI) << endl;
cout << "3.14159 * 180 / 3.14159: " << (3.14159 * 180 / 3.14159) << endl;
cout << "PI * 180 / 3.14159: " << (PI * 180 / 3.14159) << endl;
cout << "3.14159 * 180 / PI: " << (3.14159 * 180 / PI) << endl;
return 0;
}
Когда я компилирую и запускаю, я получаю следующий вывод:
pi: 3.14159
PI, in degrees: 2880
45 degrees, in rad: 0.785398
PI * 180 / PI: 2880
3.14159 * 180 / 3.14159: 180
PI * 180 / 3.14159: 180
3.14159 * 180 / PI: 2880
Кажется, мой постоянный PI работает в числителе, но не в знаменателе. Я наблюдал такое же поведение в C. Я использую gcc версии 4.6.3.
Кто-нибудь может объяснить, почему у меня такое поведение?
Макросы — это (относительно простые) текстовые замены.
Используйте скобки в своих определениях (как для включения самого макроса, так и аргументов макроса):
#define PI (atan(1) * 4)
#define radians(deg) ((deg) * PI / 180)
#define degrees(rad) ((rad) * 180 / PI)
Первый, cmath
определяет M_PI
, используйте это.
Во-вторых, макросы cpp выполняют текстовую подстановку. Что означает, что это:
#define PI atan(1) * 4
a = 1 / PI;
будет превращено в это:
a = 1 / atan(1) * 4;
до того, как компилятор c / c ++ получит шанс увидеть ваш код, и он будет обрабатывать его эквивалентно следующему:
a = (1 / atan(1)) * 4;
что не то, что вы хотите.
Ваше определение должно выглядеть так:
#define PI (atan(1) * 4)
и все должно быть хорошо.
Это не совсем странное поведение, но хорошо документированное поведение c-препроцессора.
Вы должны искать в Интернете другие подводные камни с макросами. (подсказка: передача параметров)
Вы должны использовать скобки для макросов, чтобы указать приоритет. В дополнение к этому, я думаю, что во многих случаях math.h определит для вас PI
Макросы просто выполняют подстановку текста без учета контекста, поэтому вы получите:
cout << "PI, in degrees: " << atan(1) * 4 * 180 / atan(1) * 4 << endl;
Обратите внимание на явное отсутствие паренов вокруг второго atan(1) * 4
в результате чего делится только на atan(1)
а затем умножить на 4.
Вместо этого используйте встроенные функции и глобальные переменные:
const double PI = atan(1) * 4;
double radians(double deg) { return deg * PI / 180; }
double degrees(double rad) { return rad * 180 / PI; }
Также хорошая практика: добавьте скобки вокруг всех ваших параметров:
#define radians(deg) ((deg) * PI / 180)
потому что выражение, которое вы передаете в качестве параметра, может также включать операторы.
Или даже лучше: используйте (inline-) функции вместо макросов, чтобы избежать неожиданностей с побочными эффектами, когда параметр оценивается несколько раз, как здесь:
#define sqr(x) ((x) * (x))
Единственный недостаток, который вы получаете с встроенными функциями: вы можете определить их только для одного типа (если вы не используете шаблоны C ++)