# определить преобразование из C в C # - PullRequest
8 голосов
/ 29 августа 2011

Является ли этот код C:

/* LERP(a,b,c) = linear interpolation macro, is 'a' when c == 0.0 and 'b' when c == 1.0 */
#define LERP(a,b,c)     (((b) - (a)) * (c) + (a))

http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_T.html

равным этому коду C #?

private static double LERP(double a, double b, double c) { return (((b) - (a)) * (c) + (a)); }

?

Ответы [ 6 ]

13 голосов
/ 29 августа 2011

нет. учитывать следующее:

LERP(x++,1,2);

У кода c также может быть побочный эффект увеличения x в два раза [он не определен, как упомянуто @phresnel], в то время как код c # точно определен и будет увеличивать x только один раз.

результат также может отличаться, так как первый a и второй [в макросе] могут иметь другое значение, так как он мог бы увеличиться в первом.

8 голосов
/ 29 августа 2011

Нет.Вариант C поставляется со всеми недостатками #define -macros.Напоминание:

#define LERP(a,b,c)     (((b) - (a)) * (c) + (a))

Потенциальная потеря производительности

Представьте себе чистый вызов функции при вызове #define -macro:

int fac (int x) {
    return x<=1 ? 1 : x*fac(x-1);
}

int main () {
    std::cout << LERP(fac(5), fac(2), 0);
}

Эта строка расширяетсяto:

    std::cout << (((fac(2)) - (fac(5))) * (0) + (fac(5)))

Теперь вы потенциально удвоили время выполнения для двух вызовов вашей факультативной функции, которая, по-видимому, была всего одна.

Это, безусловно, ухудшается, если вы вкладываете свой лепрпинг, как дляЭкземпляр часто встречается в некоторых ситуациях графического программирования:

int main () {
    std::cout << LERP(
                     LERP(fac(5), fac(2), 0),
                     LERP(fac(5), fac(2), 0),
                     0
                 );
}

Расширение до:

int main () {
    std::cout << LERP(
                     (((fac(2)) - (fac(5))) * (0) + (fac(5))),
                     (((fac(2)) - (fac(5))) * (0) + (fac(5)))
                     0
                 );
}

Расширение до (форматирование настроено для удобства чтения):

int main () {
    std::cout << (  (((((fac(2)) - (fac(5))) * (0) + (fac(5))))
                  - ((((fac(2)) - (fac(5))) * (0) + (fac(5)))))
                  * (c)
                 + ((((fac(2)) - (fac(5))) * (0) + (fac(5)))))
}

Принимая во внимание чистотувычислительная версия не более:

float a = LERP(fac(5), fac(2), 0);
float b = LERP(fac(5), fac(2), 0);
float c = LERP(a,b,0);

или

float fac_a = fac(5),
      fac_b = fac(2);
float a = (fac_b-fac_a)*0 + fac_a;
float fac_c = fac(5),
      fac_d = fac(2);
float a = (fac_d-fac_c)*0 + fac_c;

Итак, в двумерной установке

  1. Правильная версия:
    1. 4 вызова на fac()
    2. 4 сложения
    3. 2 умножения
  2. ´ # define` версия:
    1. 9 вызовов на fac()
    2. 8 сложений
    3. 4 умножения

Получается экспоненциальноХуже с каждым измерением, которое вы бы добавили.Иногда видны даже пятимерные Perlin Noise (3d том + время + непрерывное начальное число), , для которых некоторые выражения оцениваются как ошеломляющие 31 раз, а не только один раз! :

LERP( LERP(LERP(LERP(LERP(probe(),1,2), LERP(3,4,5), 6),
                LERP(LERP(7,8,9), LERP(10,11,12), 13),
                14),
           LERP(LERP(LERP(90,91,92), LERP(93,94,95), 96),
                LERP(LERP(97,98,99), LERP(910,911,912), 913),
                914),
           1014),
      LERP(LERP(LERP(LERP(0,1,2), LERP(3,4,5), 6),
                LERP(LERP(7,8,9), LERP(10,11,12), 13),
                14),
          LERP(LERP(LERP(90,91,92), LERP(93,94,95), 96),
               LERP(LERP(97,98,99), LERP(910,911,912), 913),
               914),
          1014),
      666)

Вы также можете увидеть предварительно обработанный код, вызвав cpp (обратите внимание на одиночное появление probe() перед).

foo@bar:~/ cpp heavy.cc

[snip] (((((((((((((((((911) - (910)) * (912) + (910))) - ((((98) - (97))* (99) + (97)))) * (913) + ((((98) - (97)) * (99) + (97))))) - (((((((94) -(93)) * (95) + (93))) - ((((91) - (90)) * (92) + (90)))) * (96) + ((((91) - (90)) * (92) + (90)))))) * (914) + (((((((94) - (93)) * (95) + (93))) - ((((91) - (90)) * (92) + (90)))) * (96) + ((((91) - (90)) * (92) + (90))))))) - ((((((((((11) - (10)) * (12) + (10))) - ((((8) - (7)) * (9) + (7)))) *(13) + ((((8) - (7)) * (9) + (7))))) - ((((((((4) - (3)) * (5) + (3))) - ((((1) - (0)) * (2) + (0)))) * (6) + ((((1) - (0)) * (2) + (0)))))) * (14) + (((((((4) - (3)) * (5) + (3))) - ((((1) - (0)) * (2) +(0)))) * (6) + ((((1) - (0)) * (2) + (0)))))))) * (1014) + ((((((((((11) - (10)) * (12) + (10))) - ((((8) - (7)) * (9) + (7)))) * (13) + ((((8) - (7)) * (9) + (7))))) - ((((((((4) - (3)) * (5) + (3))) - ((((1) - (0)) * (2) + (0)))) * (6) + ((((1) - (0)) * (2) + (0)))))) * (14) + ((((((((4) - (3)) * (5) + (3))) - ((((1) - (0)) * (2) + (0)))) *(6) + (((((1) - (0)) * (2) + (0))))))))) - (((((((((((((911) - (910))) * (912) + (910))) - ((((98) - (97)) * (99) + (97)))) * (913) + ((((98) - (97)) * (99) + (97))))) - ((((((((94) - (93)) * (95) + (93))) - ((((91) - (90))* (92) + (90)))) * (96) + ((((91) - (90)) * (92) + (90)))))) * (914) + (((((((94) - (93)) * (95) + (93))) - ((((91) - (90)) * (92) + (90)))) * (96) + ((((91) - (90)) * (92) + (90))))))) - ((((((((((11) - (10)) * (12) + (10)))- (((((8) - (7)) * (9) + (7)))) * (13) + ((((8) - (7)) * (9) + (7))))) - ((((((((4) - (3)) * (5) + (3))) - ((((1) - (пробник ())) * (2) + (пробник ())))) * (6) + (((((1) - (зонд ())) * (2) + (зонд ())))))) * (14) + (((((((4)- (3)) * (5) + (3))) - ((((1) - (пробник ())) * (2) + (пробник ())))) * (6) + ((((1) - (пробник ())) * (2) + (пробник ())))))))) * (1014) + ((((((((((11) - (10)) *(12) + (10))) - ((((8) - (7)) * (9) + (7)))) * (13) + ((((8) - (7)) * (9) + (7))))) - (((((((4) - (3)) * (5) + (3))) - ((((1) - (пробник ())) *(2) + (датчик ())))) * (6) + ((((1) - (датчик ())) * (2) + (зонд ())))))) * (14) + ((((((((4) - (3)) * (5) + (3))) - ((((1) - (датчик ())) * (2) + (датчик ())))) * (6) + ((((1) - (датчик ())) * (2) + (датчик ())))))))))) * (666) + (((((((((((((((911) - (910)) * (912) + (910))) - ((((98) - (97)) * (99) + (97)))) * (913) + ((((98) - (97)) * (99) + (97)))) - (((((((94) - (93)) * (95) + (93))) - ((((91) - (90)) * (92) + (90)))) * (96) + ((((91) - (90)) * (92) + (90)))))) * (914) + (((((((94) - (93)) * (95) + (93))) - ((((91) - (90)) * (92) + (90)))) * (96) + ((((91) - (90)) * (92) + (90))))))) - (((((((((((11) - (10)) * (12) + (10))) - ((((8) - (7)) * (9) +(7)))) * (13) + ((((8) - (7)) * (9) + (7))))) - (((((((4) - (3)) *(5) + (3))) - ((((1) - (датчик ())) * (2) + (датчик ())))) * (6) + ((((1) - (датчик())) * (2) + (зонд ())))))) * (14) + (((((((4) - (3)) * (5) + (3))) - ((((1) - (зонд ())) * (2) + (зонд ())))) * (6) + ((((1) - (зонд ())) * (2) + (зонд())))))))) * (1014) + (((((((((((11) - (10)) * (12) + (10))) - ((((8) -(7)) * (9) + (7)))) * (13) +((((8) - (7)) * (9) + (7))))) - (((((((4) - (3)) * (5) + (3))) - ((((1) - (зонд ())) * (2) + (зонд ())))) * (6) + ((((1) - (зонд ())) * (2) + (зонд())))))) * (14) + (((((((4) - (3)) * (5) + (3))) - ((((1) - (пробник ())) * (2) + (пробник ())))) * (6) + ((((1) - (пробник ())) * (2) + (пробник ())))))))))

Опять же, полный источник здесь .

Потенциальное неопределенное поведение

Вы можете поместить в него злые вещи:

LERP(++a,b,c)

, который расширяется до

(((b) - (++a)) * (c) + (++a))

, что является неопределенным поведением в C¹(и в C ++, кстати).a может быть увеличено в два раза, или может быть увеличено один раз, или может быть сгенерировано исключение, которое говорит Debug-Runtime-Exception: Invoked Undefined Behaviour, или компилятор достаточно умен, чтобы отклонить этот код, или что-то еще.

Неопределенное поведениеисходит из того факта, что стандарт C99 (и C ++ 2003 тоже) не допускает многократного изменения значения до достижения следующей точки последовательности .

ID загрязнения и инфекции

(Это более уместно, если вы преобразуете C # в вариант макроса.)

* Макро-имя #define загрязняет и заражает всю единицу перевода от точки определения до конца единицы или ее неопределенности.

foo@bar:~/ cat main.cc
// Orbiter Physics Sim
#include "lerp.h"

int main () {
    const int LERP = 2; // Linear Extrasolar Resonance Potential.
}

foo@bar:~/ g++ main.cc
main.cc:5:15: error: expected unqualified-id before ‘=’ token

Подробнее ...

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

¹: C99 (ISO / IEC 9899: TC2), J.2, «Неопределенное поведение»: Between two sequence points, an object is modified more than once, or is modified and the prior value is read other than to determine the value to be stored (6.5).

5 голосов
/ 29 августа 2011

Технически, нет, они не равны. Макрос C может принимать любой тип: int, float, byte и т. Д.

Ваша версия C # может обрабатывать только двойные, без явных приведений. Вам нужно будет добавить перегрузки по мере необходимости для других типов.

2 голосов
/ 29 августа 2011

Чтобы получить то, что у вас было в C, вы можете использовать:

delegate double ValueGetter();
static double LERP(ValueGetter a, ValueGetter b, ValueGetter c) { return (b() - a()) * c() + a(); }

Но я согласен с amit.Это может быть то же самое, что вы имели в C (но только для double, а не для других типов), но это может быть не то, что вы действительно хотите .. (с делегатом вы можете установить 'i ++' как, а не только результат i ++)

2 голосов
/ 29 августа 2011

Это в основном эквивалентно, да.Вы также можете избавиться от некоторых скобок:

private static double LERP(double a, double b, double c) { return (b - a) * c + a; }
1 голос
/ 29 августа 2011

Да, но вы можете написать это более просто:

return (b - a) * c + a; 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...