#определить преобразование из C в C#

это код 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 ответов


нет. рассмотрим следующее:

LERP(x++,1,2);

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

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


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

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


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

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

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

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

эта строка расширяется до:

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

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

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

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 умножений

это становится экспоненциально хуже с каждым измерением, которое вы добавляете. Даже пятимерный!--73-->Перлин Шума иногда видно (объем 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))

который является неопределенным поведением в C1 (и в 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

больше ...

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

1: 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).


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

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


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

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

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

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

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


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

return (b - a) * c + a;