[英]How does the below C macro example compile?
The code below does not give the correct output.下面的代码没有给出正确的 output。
#include <stdio.h>
#define PI 22/7
#define AREA(r) PI * r * r
int main() {
printf("Pi: %d\n", PI);
printf("Area: %d\n", AREA(8));
return 0;
}
Whereas the code below gives the correct (nearest) output.而下面的代码给出了正确的(最近的)output。
#include <stdio.h>
#define PI 22/7
#define AREA(r) r * r * PI
int main() {
printf("Pi: %d\n", PI);
printf("Area: %d\n", AREA(8));
return 0;
}
How exactly do these code differ?这些代码究竟有何不同?
Why is it so?为什么会这样? I am not able to understand the difference between how both the above codes would give different answers.我无法理解上述两种代码如何给出不同答案之间的区别。
The problem is your PI
macro:问题是您的PI
宏:
#define PI 22/7
There are two problems with this: (1) It's not wrapped in parentheses, to guard it against parts of it binding more tightly to other parts of the expression in which it's expanded, and (2) It's doing integer division.这有两个问题:(1) 它没有包含在括号中,以防止它的某些部分更紧密地绑定到它展开的表达式的其他部分,以及 (2) 它正在做 integer 除法。
If you have 22/7
by itself, it's just 3
.如果你有22/7
本身,它只是3
。 Presumably that's not what you want.大概这不是你想要的。 But the AREA
macro, which also has problems, looks like this:但是同样有问题的AREA
宏看起来像这样:
#define AREA(r) r * r * PI
With PI
expanded, this is equivalent to:随着PI
展开,这相当于:
#define AREA(r) r * r * 22/7
Assuming r
is a simple variable or constant, this is equivalent to:假设r
是一个简单的变量或常量,这相当于:
#define AREA(r) (r * r * 22) / 7
If r
is an integer, it's still doing integer division, but it's only truncating at the end, so the result will be more accurate.如果r
是一个integer,它仍然在做integer除法,但它只是在最后截断,所以结果会更准确。
Note that AREA
has two problems of its own: (1) It's not wrapping its argument, r
, in parentheses, and (2) It's not wrapping the expression as a whole in parentheses.请注意, AREA
自身有两个问题:(1) 它没有将其参数r
括在括号中,以及 (2) 它没有将整个表达式括在括号中。
For problem (1), imagine passing something like 2+3
as r
.对于问题 (1),想象将2+3
作为r
。 Since multiplication associates more tightly than addition, this clearly won't do what you want.由于乘法关联比加法更紧密,这显然不会做你想要的。
If you want an accurate result, you would normally use an accurate floating-point value for pi, rather than the extremely inaccurate approximation 22/7.如果您想要一个准确的结果,您通常会为 pi 使用一个准确的浮点值,而不是极其不准确的近似值 22/7。 But if you do want to use 22/7, you should still use a floating point value for it:但是如果你确实想使用 22/7,你仍然应该为它使用一个浮点值:
#define PI (22.0/7.0)
Then you can fix AREA
:然后你可以修复AREA
:
#define AREA(r) (PI * (r) * (r))
This will properly protect the arguments and final results from reassociating after expansion, but note that the result will have type double
.这将适当地保护 arguments 和最终结果在扩展后不会重新关联,但请注意结果的类型为double
。 Also note that its argument, r
, is evaluated twice.另请注意,它的参数r
被计算了两次。
But a better approximation to pi is preferable.但是更好地近似于 pi 是可取的。 On Linux, math.h
exports M_PI
, which is far more accurate.在 Linux 上, math.h
导出M_PI
,这要准确得多。
Try expanding the macro.尝试扩展宏。 The first one is 22 / 7 * 8 * 8. All values are integers, so you get rounding errors and the calculation is basically 3 * 8 * 8 = 192.第一个是 22 / 7 * 8 * 8。所有值都是整数,所以会出现舍入误差,计算结果基本上是 3 * 8 * 8 = 192。
The second expression is 8 * 8 * 22 / 7. So the calculation is 1408 / 7 = 201.第二个表达式是 8 * 8 * 22 / 7。所以计算结果是 1408 / 7 = 201。
Multiplication and division is done from left to right.乘法和除法是从左到右进行的。 So, AREA(8) in the first example expands to 22/7 * 8 * 8 which is evaluated like so: (((22/7) * 8) * 8), ie 3 * 8 * 8 = 192.因此,第一个示例中的 AREA(8) 扩展为 22/7 * 8 * 8,其计算方式如下:(((22/7) * 8) * 8),即 3 * 8 * 8 = 192。
The other variant gives: (((8*8)*22)/7) = (64 * 22)/7 = 1408/7 = 201.另一个变体给出:(((8*8)*22)/7) = (64 * 22)/7 = 1408/7 = 201。
If you want better precision, use floating point, ie #define PI (22.0/7.0) or, why not use a better approximation of pi: #define PI (3.141593)如果您想要更高的精度,请使用浮点数,即 #define PI (22.0/7.0) 或者,为什么不使用更好的 pi 近似值:#define PI (3.141593)
But then you should use %f in printf. (as commented by Jabberwocky)但是你应该在 printf 中使用 %f。(正如 Jabberwocky 评论的那样)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.