#定义从 C 到 C# 的转换

发布于 2024-12-01 16:13:13 字数 495 浏览 1 评论 0原文

这是 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)); }

Is this C code:

/* 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

Equals this C# code?

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

?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

雨落星ぅ辰 2024-12-08 16:13:13

不。
考虑以下内容:

LERP(x++,1,2);

C 代码还可能具有将 x 增加两次的副作用 [如 @phresnel 所述,它是未定义的],而 C# 代码则完美定义,并且只会增加 x 一次。

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

no.
consider the following:

LERP(x++,1,2);

The c code might also have a side effect of increasing x twice [it is undefined as mentioned by @phresnel], while the c# code is perfectly defined, and will increase x only once.

the result also might be different, since the first a and the second one [in the macro] might have a different value, since it might have increased in the first one.

信仰 2024-12-08 16:13:13

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

随着您添加的每个维度,情况会呈指数级恶化。有时甚至会看到五维柏林噪声(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

[剪断] ((((((((((((((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-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,“未定义的行为”:在两个序列点之间,对象被修改更多不止一次,或被修改并读取先前的值,而不是确定要存储的值(6.5)。

No. The C variant comes with all the deficiencies of #define-macros. Reminder:

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

Potential Waste of Performance

Imagine a pure function call in an invokation of the #define-macro:

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

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

That line expands to:

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

Now you have potentially doubled the runtime for two invokations of your faculty function that was perviously just one.

This surely gets worse if you nest your lerping, as is for instance common in some graphics programming situations:

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

Expanding to:

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

Expanding to (formatting tweaked for readability):

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

Whereas the clean version does computationally not more than:

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

or

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;

So in a two dimensional setup

  1. Proper version:
    1. 4 calls to fac()
    2. 4 additions
    3. 2 multiplications
  2. ´#define` version:
    1. 9 calls to fac()
    2. 8 additions
    3. 4 multiplications

It gets exponentially worse with every dimension you'd add. Even five-dimensional Perlin Noise is sometimes seen (3d volume + time + continous seed), for which some expressions are evaluated freaking 31 times, instead of just once!:

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)

You can also see the preprocessed code by invoking cpp (note the single appearance of probe() before).

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

Again, full source is here.

Potential Undefined Behaviour

You can put evil stuff into it:

LERP(++a,b,c)

which expands to

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

which is undefined behaviour in C¹ (and in C++, btw). a might be increased twice, or it might be increased once, or an exception might be thrown that says Debug-Runtime-Exception: Invoked Undefined Behaviour, or the compiler is smart enough to reject that code, or whatever.

The undefined behaviour comes from the fact that the C99-standard (and C++2003, too) does not allow a value to be modified multiple times before reaching the next Sequence Point.

ID pollution and infection

(This is more relevant if you'd convert the C# into the macro variant.)

The #define-macro-name pollutes and infects the whole unit of translation from the point of definition to either the end-of-unit or its undefinition.

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

More ...

  • Macros are generic, but not typesafe. The macro-writer has no (clean) opportunity to put constraints on the types for which that macro should be valid.
  • Macros don't have scope, though this is already implied by the last section

¹: C99 (ISO/IEC 9899:TC2), J.2, "Undefined Behaviour": 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).

安穩 2024-12-08 16:13:13

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

您的 C# 版本只能处理 double,而无需显式强制转换。您需要根据其他类型的需要添加重载。

Technically, no they are not equal. The C macro can take any type: int, float, byte, etc.

Your C# version can only handle double, without explicit casts. You'd need to add overloads as needed for other types.

枯寂 2024-12-08 16:13:13

基本上是等价的,是的。您还可以去掉一些括号:

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

It's basically equivalent, yes. You can also get rid of some parentheses:

private static double LERP(double a, double b, double c) { return (b - a) * c + a; }
素罗衫 2024-12-08 16:13:13

要获得 C 语言中的内容,您可以使用:

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

但我同意阿米特。这可能与您在 C 中的情况相同(但仅适用于 double,不适用于其他类型),但它可能不是您真正想要的..(使用委托,您可以将 'i++' 设置为 a,而不仅仅是 i++ 的结果)

To get what you had in C you may use:

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

But I agree with amit. This might be the same you had in C (but solely for double not for other types) still it might not be what you really want.. (with delegate you could set 'i++' as a, not only result of i++)

执着的年纪 2024-12-08 16:13:13

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

return (b - a) * c + a; 

Yes, but you can write it more simple:

return (b - a) * c + a; 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文