2017-02-21 17 views
1

Чтобы ускорить работу моей программы, я хотел бы представить функцию для вычисления остатка деления с плавающей запятой (где коэффициент естественно, очевидно).Можно ли объявить переменную в функции в выражении #define

Поэтому я следующие простые функции:

double mfmod(double x,double y) { 
    double a; 
    return ((a=x/y)-(int)a)*y; 
    } 

Как я слышал, я мог бы ускорить еще больше, поставив эту функцию в пункте #define, но переменная a делает это довольно сложно. В данный момент я здесь:

#define mfmod(x,y) {   \ 
    double a;     \ 
    return ((a=x/y)-(int)a)*y; \ 
    } 

Но попытка запустить это дает проблемы из-за переменной.

Проблемы заключаются в следующем: немного дальше я пытаюсь запустить эту функцию:

двойной тест = mfmod (е, дел);

И это невозможно скомпилировать из-за сообщения об ошибке type name is not allowed. (для вашей информации, f и div являются doubles)

Кто-нибудь знает, как это сделать? (Если это вообще возможно) (я работаю с Windows, более точно Visual Studio, а не с GCC)

+0

За исключением вопроса о последовательности (вы не знаете, будет ли задание выполнено до того, как значение 'a' будет считано для вычитания) и что переменная' a' не будет доступна за пределами области блока , и что он вернется из любой функции, в которой он находится (что может и не быть проблемой), ваш макрос выглядит нормально. Вы должны дать нам нечто большее, чем «дает проблемы». Пожалуйста, [прочитайте о том, как задавать хорошие вопросы] (http://stackoverflow.com/help/how-to-ask) и узнайте, как создать [Минимальный, полный и проверенный пример] (http://stackoverflow.com/помощь/mcve). –

+0

Обратите внимание, что 'x' &' y' могут быть выражениями, и вам придется их скопировать, чтобы избежать побочных эффектов. –

+2

@Someprogrammerdude нет у него есть 'return' –

ответ

2

Как я слышал, я мог бы ускорить еще больше, поставив эту функцию в пункте #define

Я думаю, вы, должно быть, неправильно поняли. Разумеется, совет заключался в том, чтобы реализовать поведение как a (#define d) макрос, вместо как функция. Ваш макрос синтаксически действителен, но код, полученный при его расширении, не является подходящей заменой для вызова вашей функции.

Определение этого как макроса в основном является способом ручного ввода тела функции, но имеет некоторые недостатки. В частности, в стандарте C макрос, который должен быть расширен до выражения (то есть тот, который может использоваться как значение), не может содержать кодовый блок и, следовательно, не может содержать объявления переменных. Это, в свою очередь, может сделать невозможным избежать многократной оценки аргументов макроса, что является большой проблемой.

Вот один способ, которым Вы могли бы написать макрос:

#define mfmod(x,y) (((x)/(y)) - (int) ((x)/(y))) 

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

Если вы были готовы изменить режим использования, чтобы макрос задавал переменную результата вместо того, чтобы расширять ее до выражения, тогда вы можете обойти многие проблемы. @BnBDim предоставил первый разрез в этом, но он страдает от некоторых из тех же типов и проблем с множественной оценкой, что и выше.Вот как вы можете сделать это, чтобы получить тот же результат, что ваша функция:

#define mfmod(x, y, res) do {   \ 
    double _div = (y);     \ 
    double _quot = (double) (x)/_div; \ 
    res = (_quot - (int) _quot) * _div; \ 
} while (0) 

Обратите внимание, что он берет на себя, чтобы ссылаться на аргументы один раз, а также в скобках, но и для res, который должен быть именующее. Вы могли бы использовать его так же, как пустую функцию, а не как стоимость возвращения функции:

double test; 
mfmod(f, div, test); 

Это все еще позволяет себе незначительный, но неизбежный риск поломки в том случае, если один из фактических аргументов макрос сталкивается с одна из переменных, объявленных внутри блока кода, который она предоставляет. Использование имен переменных с префиксом подчеркивания предназначено для минимизации этого риска.


В целом, я был бы склонен идти с функцией вместо этого и позволить компилятору обрабатывать вставку. Если вы хотите, чтобы это побудило компилятор, вы можете рассмотреть возможность объявления функции inline, но, скорее всего, он не понадобится такой намек, и он не обязан отмечать его.

Или лучше, просто используйте fmod() до тех пор, пока вы не определите, что это является узким местом.

+0

почему 'do/while (0)' вместо простых фигурных скобок? –

+1

@ Jean-FrançoisFabre, потому что тогда вы не получите предупреждение о пустой инструкции, когда будете следовать вызову макроса точкой с запятой ('mfmod (1.5, .6, result);'), и на самом деле точка с запятой обязательный. Это очень условно для макросов, подобных операторам. –

+0

Да, конечно, имеет смысл. –

0

Вы могли бы использовать это обходный

#define mfmod(x,y,res)  \ 
    do {      \ 
     double a=(x)/(y);  \ 
     res = (a-(int)a)*(y); \ 
    } while(0) 
+0

cache 'y', поэтому он не оценивается дважды (проблема с побочным эффектом), в противном случае может работать. –

+0

@ Jean-FrançoisFabre 'y' никогда не будет оцениваться дважды. Это значение C. 'y' является числовым значением. Кэширование будет оптимизировано, потому что оно просто потеряет больше пространства стека с дополнительным значением для хранения того же значения, что и сохраненный параметр. Если он не будет оптимизирован, это сделает программу хуже. – Ideasthete

+0

'mfmod (x, sqrt (y), res)': вы увидите, что 'sqrt (y)' оценивается дважды, потому что есть вызов функции. Это то, что я имел в виду. –

1

Для ответа на заявленный вопрос: да, вы можете объявлять переменные в определенных макро «функции», как тот, который вы работаете, в определенных ситуациях. Некоторые из других ответов показали примеры того, как это сделать.

Проблема с вашим текущим #define заключается в том, что вы сообщаете ему return что-то среднее от вашего кода, и вы не делаете макрос, который расширяется так, как вы, вероятно, хотите. Если я использую макрос так:

... 
double y = 1.0; 
double x = 1.0; 
double z = mfmod(x, y); 
int other = (int)z - 1; 
... 

Это будет расширяться:

... 
double y = 1.0; 
double x = 1.0; 
double z = { 
    double a; 
    return ((a=x/y)-(int)a)*y; 
}; 
int other = (int)z - 1; 
... 

Функция (если она компилируется) никогда бы не перейти за пределы инициализации z, потому что он вернется в середина макроса. Вы также пытаетесь присвоить блок кода z.

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

__attribute__((const)) 
extern inline double mfmod(const double x, const double y) { 
    const double a = x/y; 
    return (a - (int)a) * y; 
} 

это не только чище, яснее и проще отлаживать, чем макрос, он имеет дополнительное преимущество быть объявлен с атрибутом const, который будет советовать компилятором, что последующие вызовы функции с те же аргументы должны возвращать одно и то же значение, которое может вызвать повторные вызовы полностью оптимизированной функции, тогда как макрос будет оцениваться каждый раз (концептуально). Честно говоря, даже использование локального двойника для кэширования результата разделения, вероятно, является преждевременной оптимизацией, поскольку компилятор, вероятно, оптимизирует это.Если бы это было мне, и я абсолютно должен был иметь макрос, чтобы сделать это, я хотел бы написать это следующим образом:

#define mfmod(x, y) (((x/y)-((int)(x/y)))*y) 

Там почти наверняка не будет заметного снижения производительности при оптимизации для этого разделения в два раза. Если бы это было, я бы использовал встроенную функцию выше. Я оставлю это вам, чтобы выполнить бенчмаркинг.

+0

Спасибо за подробный ответ, но решение с использованием встроенной функции, как вы описываете, похоже, не работает, поскольку мой компилятор (Visual Studio), похоже, не понимает ключевое слово '__attribute__', и без этого производительность равна обычной строке функции. – Dominique

+0

@ Dominique - опять же, это не похоже на то, что вы сравнили это. Вы сказали: «Производительность равна нормальным встроенным функциям». Производительность встроенной функции более или менее идентична производительности макроподстановки. Весь смысл вложения заключается в том, что он избегает служебных вызовов функций. Похоже, вы пытаетесь оптимизировать проблему, которой у вас нет. Оцените мою встроенную функцию (без атрибута) против любого макроса и посмотрите, как она работает. Макросы редко являются правильным ответом для решения проблем производительности, и они почти наверняка не являются ответом здесь. – Ideasthete