#define从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)); } 

没有。 考虑以下:

LERP(x++,1,2);

c代码也可能有两倍增加x的副作用[它是@phresnel所提到的未定义],而c#代码是完美定义的,并且只会增加x一次。

结果也可能不同,因为第a和第二个[在宏中]可能具有不同的值,因为它可能在第一个中增加。

不会.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); } 

那条线扩展到:

  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次乘法

对于您添加的每个维度,它会呈指数级变差。 甚至有时会看到五维Perlin噪声 (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) - (probe()))*(2)+(probe()))))* (6)+(((((1) - (probe()))*(2)+(probe()))))))*(14)+((((((((4) - (3) )*(5)+(3))) - ((((1) - (probe()))*(2)+(probe()))))*(6)+((((1) - (probe())*(2)+(probe()))))))))*(1014)+(((((((11) - (10))*(12)+ (10))) - ((((8) - (7))*(9)+(7))))*(13)+(((((8) - (7))*(9)+( 7))))) - ((((((((4) - (3))*(5)+(3))) - (((((1) - (probe()))*(2)+ (probe()))))*(6)+(((((1) - (probe()))*( 2)+(probe()))))))*(14)+((((((((4) - (3))*(5)+(3))) - ((((1) - (probe()))*(2)+(probe()))))*(6)+(((((1) - (probe()))*(2)+(probe())))) )))))*(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) - (probe()))*(2)+(probe()))))*(6)+(((((1) - (probe())) *(2)+(probe()))))))*(14)+((((((((4) - (3))*(5)+(3))) - ((((1 ) - (probe()))*(2)+(probe()))))*(6)+(((((1) - (probe()))*(2)+(probe())) )))))*(1014)+(((((((((((11) - (10))*(12)+(10))) - ((((8) - (7)) *(9)+(7))))*(13)+((((8 ) - (7))*(9)+(7))))) - (((((((4) - (3))*(5)+(3))) - ((((1) - (probe()))*(2)+(probe()))))*(6)+(((((1) - (probe()))*(2)+(probe()))) )))*(14)+((((((((4) - (3))*(5)+(3))) - (((((1) - (probe()))*(2) +(probe()))))*(6)+(((((1) - (probe()))*(2)+(probe())))))))))))

同样,完整的来源在这里 。

潜在的未定义行为

你可以把邪恶的东西放进去:

 LERP(++a,b,c) 

扩展到

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

这是C¹(以及C ++,btw)中未定义的行为。 a可能会增加两次,或者可能会增加一次,或者抛出一个exception,表示Debug-Runtime-Exception: Invoked Undefined Behaviour ,或者编译器足够智能拒绝该代码,或者其他什么。

未定义的行为来自于C99标准(以及C ++ 2003)在到达下一个序列点之前不允许多次修改值的事实。

身份证污染和感染

(如果您将C#转换为宏变体,则更相关。)

#define -macro-name污染并感染整个翻译单元,从定义点到单元结束或其不定义。

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

从技术上讲,不,他们不平等。 C宏可以采用任何类型: intfloatbyte等。

你的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(); } 

但我同意amit。 这可能与你在C中的相同(但仅限于其他类型的双倍)仍然可能不是你真正想要的…(使用委托你可以设置’i ++’作为,而不仅仅是i ++的结果)

是的,但你可以写得更简单:

 return(b  -  a)* c + a;