M_PI работает с math.h, но не с cmath в Visual Studio

96

Я использую Visual Studio 2010. Я читал, что на C ++ лучше использовать <cmath>, чем <math.h>.

Но в программе, которую я пытаюсь написать (консольное приложение Win32, пустой проект), я пишу:

#define _USE_MATH_DEFINES
#include <math.h>

он компилируется, а если я напишу

#define _USE_MATH_DEFINES
#include <cmath>

это не с

ошибка C2065: 'M_PI': необъявленный идентификатор

Это нормально? Имеет ли значение, использую я cmath или math.h? Если да, то как заставить его работать с cmath?

ОБНОВЛЕНИЕ : если я определю _USE_MATH_DEFINES в графическом интерфейсе, он работает. Есть какие-нибудь подсказки, почему это происходит?

гиперкнот
источник
Ваши исходные файлы .c или .cpp?
Swiss
1
Швейцарцы: здесь неважно.
rubenvb
Очень странно ... Я могу подтвердить, что у меня такая же проблема с VS2010 ... я изучаю, что мешает прохождению определения ... это должно быть где-то undef'd ... но я не могу понять, где
Goz
С x86 будет жаловаться на ошибку C2065. С x64 то ошибки нет.
user2616989

Ответы:

117

Интересно, что я проверил это в своем приложении и получил ту же ошибку.

Я потратил некоторое время, проверяя заголовки, чтобы увидеть, есть ли что-нибудь, что не соответствует, _USE_MATH_DEFINESи ничего не нашел.

Итак, я переместил

#define _USE_MATH_DEFINES
#include <cmath>

быть первым в моем файле (я не использую PCH, поэтому, если вы используете, вам придется иметь его после #include "stdafx.h"), и вдруг он отлично компилируется.

Попробуйте переместить его выше по странице. Совершенно не уверен, почему это может вызвать проблемы.

Изменить : понял это. Это #include <math.h>происходит внутри охранников заголовков cmath. Это означает, что что-то выше в списке #includes включено cmathбез #defineуказанного. math.hспециально разработан таким образом, чтобы вы могли включить его снова с этим определением, которое теперь изменено на добавление M_PIи т. д. Это НЕ относится к cmath. Итак, вам нужно убедиться, что вы#define _USE_MATH_DEFINES вы включили что-либо еще. Надеюсь, это проясняет вам это :)

В противном случае math.hвы используете нестандартный C / C ++, как уже указывалось :)

Изменить 2 : Или, как указывает Дэвид в комментариях, просто сделайте себе константу, которая определяет значение, и в любом случае у вас есть что-то более портативное :)

Гоз
источник
Определив это раньше, stdafx.hэто проблема OP, с которой я сталкивался раньше.
Alok Save
@Als: Нет, дело не в этом ... взломал это и объяснил в моем редактировании выше :)
Goz
Ну, это было первое, что нужно было сделать, держите его выше всех других заголовков. Я попросил OP сделать то же самое .... В любом случае удаляю свой ответ, так как ваш ответ говорит фактическую причину, почему он должен быть перед стандартными заголовками.
Alok Save
3
Вы могли бы получить такое поведение, например, если бы что-то еще случилось с #include <math.h> до вас. Тем не менее, в стандарте нет ничего, что говорило бы, что <cmath> должен включать <math.h>, или что он должен включать нестандартные определения в <math.h>. К сожалению, M_PI нестандартен. Что касается переносимости, лучше всего определить его самостоятельно. А еще лучше сделать его значением, const static doubleа не #defined.
Дэвид Хаммен
1
@David Hammen: Согласен .. Определить это самому - определенно самый удобный вариант :)
Goz
14

Рассмотрите возможность добавления переключателя / D_USE_MATH_DEFINES в командную строку компиляции или определения макроса в настройках проекта. Это перетащит символ во все доступные темные углы включаемых и исходных файлов, оставляя ваш исходный код чистым для нескольких платформ. Если вы установите его глобально для всего проекта, вы не забудете его позже в новом файле (ах).

Thinkeye
источник
Вероятно, это хороший ответ при работе из VisualStudio, но обратите внимание, что он не решил для меня проблему при компиляции через командную строку Matlab mex (я использовал mex -D_USE_MATH_DEFINES). /Y-Помогло только добавление smewhere в какой-то файл mexoptions Matlab ...
aka.nice
10

Это работает для меня:

#define _USE_MATH_DEFINES
#include <cmath>
#include <iostream>

using namespace std;

int main()
{
    cout << M_PI << endl;

    return 0;
}

Компилирует и печатает piкак следует:cl /O2 main.cpp /link /out:test.exe .

Должно быть несовпадение кода, который вы опубликовали, и кода, который вы пытаетесь скомпилировать.

Убедитесь, что не вставляются предварительно скомпилированные заголовки перед вашим #define.

rubenvb
источник
Какую версию VisualStudio вы используете?
Goz
Эта же программа отлично работала для меня с использованием компилятора командной строки Visual C ++ 2010 Express Edition. Единственная разница в том, что я использовал std :: printf () из <cstdio> вместо std :: cout из <iostream>.
5
Да, я понял ... это потому, что maths.h вызывается изнутри охранников заголовка cmath ... так что maths.h уже был включен из более раннего заголовка без набора #define :)
Goz
4

Это по-прежнему проблема в VS Community 2015 и 2017 при создании консольных приложений или приложений для Windows. Если проект создается с предварительно скомпилированными заголовками, предварительно скомпилированные заголовки явно загружаются до любого из #includes, поэтому даже если #define _USE_MATH_DEFINES является первой строкой, он не будет компилироваться. #including math.h вместо cmath не имеет значения.

Единственные решения, которые я могу найти, - это либо начать с пустого проекта (для простых консольных или встроенных системных приложений), либо добавить / Y- в аргументы командной строки, что отключает загрузку предварительно скомпилированных заголовков.

Для получения информации об отключении предварительно скомпилированных заголовков см., Например, https://msdn.microsoft.com/en-us/library/1hy7a92h.aspx

Было бы хорошо, если бы MS изменила / исправила это. Я преподаю вводные курсы программирования в большом университете, и объяснения этого новичкам никогда не улавливаются, пока они не совершат ошибку и не будут бороться с ней в течение дня или около того.

пользователь3533658
источник
Я подтверждаю, что взлом / Y- сработал для меня, для кода C #include <math.h>
aka.nice
1
В VS это вообще не проблема. _USE_MATH_DEFINESдолжен быть определен до включения каких-либо заголовков. Обычно либо через настройки проекта, либо через заголовок конфигурации. Неверно предполагать, что простое размещение его в первой строке приведет к его определению до всех заголовков.
user7860670
1

Согласно документации Microsoft о математических константах :

Файл ATLComTime.hвключается, math.hкогда ваш проект построен в режиме выпуска. Если вы используете одну или несколько математических констант в проекте, который также включает ATLComTime.h, вы должны определить их _USE_MATH_DEFINESперед включением ATLComTime.h.

Файл ATLComTime.hможет быть косвенно включен в ваш проект. В моем случае один из возможных порядков включения был следующим:

проект "stdafx.h"<afxdtctl.h><afxdisp.h><ATLComTime.h><math.h>

αλεχολυτ
источник
Это может объяснить, почему / Y- (отключить stdafx.h) решит проблему, однако остается объяснить, почему предоставление -D_USE_MATH_DEFINESв настройках компилятора по умолчанию недостаточно для решения проблемы ... Поскольку компиляция выполнялась с помощью команды Matlab mex для моего собственного проблема, это не так очевидно для отслеживания ...
aka.nice
1

С CMake это было бы просто

add_compile_definitions(_USE_MATH_DEFINES)

в CMakeLists.txt.

FLUXчастица
источник
0

Как было предложено пользователем 7860670, щелкните проект правой кнопкой мыши, выберите свойства, перейдите к C / C ++ -> Препроцессор и добавьте _USE_MATH_DEFINES в определения препроцессора.

Вот что у меня сработало.

Ден-Джейсон
источник