我有一个计算poylgnárea和perimeter的程序,程序接收带坐标的文本文件并计算面积。
我对计算有一些问题。现在我试图比较双打,我不明白为什么它不起作用。
我有一个包含3行的文本文件:
1.0 2.5 5.1 5.8 5.9 0.7
1.2 4.1 5.1 5.8 6.8 1.9 2.9 0.2
1.7 4.9 5.1 5.8 7.0 2.8 4.8 0.1 1.5 1.4
我希望这会产生结果:
double expectedarea = 11.77;
double expectedperimeter = 15.64;
double expectedarea1 = 18.10;
double expectedperimeter1 = 17.02;
double expectedarea2 = 21.33;
double expectedperimeter2 = 16.60;
所以我希望3个案例中出现“Arrays is same”这个消息,因为我给出了正确的值,但是我得到的消息数组在3个案例中是不同的。
你明白为什么我总是得到消息数组不同?
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
enum {x, y};
typedef struct triangle {
double v1[2];
double v2[2];
double v3[2];
} triangle;
double area(triangle a);
double perimeter(double *vertices, int size);
double side(double *p1, double *p2);
double expectedarea = 11.77;
double expectedperimeter = 15.64;
double expectedarea1 = 18.10;
double expectedperimeter1 = 17.02;
double expectedarea2 = 21.33;
double expectedperimeter2 = 16.60;
int main()
{
int idx;
int triangles;
int index;
int xycount;
double xy;
double triangle_area;
double polygon_area;
double perim;
double polygon_vertices[50] = {0.0};
triangle a;
FILE* data;
char line[256];
char* token;
if ((data = fopen("test.txt", "r")) == NULL) {
fprintf(stderr, "can't open data file\n");
exit (EXIT_FAILURE);
}
while (fgets(line, sizeof (line), data)){
xycount = 0;
polygon_area = 0;
line[strlen(line) - 1] = 0;
token = strtok(line, " ");
while (token != NULL){
xy = atof(token);
token = strtok(NULL, " ");
polygon_vertices[xycount++] = xy;
}
idx = 0;
triangles = (xycount / 2) - 2;
for (index = 2, idx = 0;idx < triangles;index += 2, ++idx){
a.v1[x] = polygon_vertices[0];
a.v1[y] = polygon_vertices[1];
a.v2[x] = polygon_vertices[index + 0];
a.v2[y] = polygon_vertices[index + 1];
a.v3[x] = polygon_vertices[index + 2];
a.v3[y] = polygon_vertices[index + 3];
triangle_area = area(a);
polygon_area += triangle_area;
}
printf("area=%f\t", polygon_area);
perim = perimeter(polygon_vertices, xycount);
printf("perimeter=%f\n", perim);
if(polygon_area == expectedarea && perim == expectedperimeter) {
printf("Arrays are the same");
}
if(polygon_area == expectedarea1 && perim == expectedperimeter1) {
printf("Arrays are the same");
}
if(polygon_area == expectedarea2 && perim == expectedperimeter2) {
printf("Arrays are the same");
}
else {
printf("Arrays are the different"); }
}
fclose(data);
return 0;
}
/* calculate triangle area with Heron's formula */
double area(triangle a)
{
double s1, s2, s3, S, area;
s1 = side(a.v1, a.v2);
s2 = side(a.v2, a.v3);
s3 = side(a.v3, a.v1);
S = (s1 + s2 + s3) / 2;
area = sqrt(S*(S - s1)*(S - s2)*(S - s3));
return area;
}
/* calculate polygon perimeter */
double perimeter(double *vertices, int size)
{
int idx, jdx;
double p1[2], p2[2], pfirst[2], plast[2];
double perimeter;
perimeter = 0.0;
/* 1st vertex of the polygon */
pfirst[x] = vertices[0];
pfirst[y] = vertices[1];
/* last vertex of polygon */
plast[x] = vertices[size-2];
plast[y] = vertices[size-1];
/* calculate perimeter minus last side */
for(idx = 0; idx <= size-3; idx += 2)
{
for(jdx = 0; jdx < 4; ++jdx)
{
p1[x] = vertices[idx];
p1[y] = vertices[idx+1];
p2[x] = vertices[idx+2];
p2[y] = vertices[idx+3];
}
perimeter += side(p1, p2);
}
/* add last side */
perimeter += side(plast, pfirst);
return perimeter;
}
/* calculate length of side */
double side(double *p1, double *p2)
{
double s1, s2, s3;
s1 = (p1[x] - p2[x]);
s2 = (p1[y] - p2[y]);
s3 = (s1 * s1) + (s2 * s2);
return sqrt(s3);
}
答案 0 :(得分:1)
在测试OP的代码时,发布的列表在0.7之后有一个空格。通常情况下,此空白区域不是问题,但我保存文件text.txt
会导致该行以" \r\n"
结束,而'\r'
会创建其他令牌。 strtok(NULL, " \n\r\t")
的其他空格字符解决了这个问题。
<1.0 2.5 5.1 5.8 5.9 0.7 >
<1.2 4.1 5.1 5.8 6.8 1.9 2.9 0.2>
<1.7 4.9 5.1 5.8 7.0 2.8 4.8 0.1 1.5 1.4>
根据列表的保存方式,最后一行可能会也可能不会以'\n'
结尾。 OP成为使用以下内容删除'\n'
的方法成为问题,因为它可能会将最后一行显示为"1.7 4.9 5.1 5.8 7.0 2.8 4.8 0.1 1.5 1."
(最后4
丢失)。
line[strlen(line) - 1] = 0;
最好使用下面的内容,不需要最终'\n'
才能正常工作。
line[strcspn(line, "\n")] = '\0';
OP的代码在其xy
列表中创建了一个太多的坐标。由于其他白色空间,它也有潜在的麻烦。添加了更完整的列表。
while (token != NULL) {
xy = atof(token);
polygon_vertices[xycount++] = xy;
token = strtok(NULL, " \n\r\t"); // more white-spaces.
if (token == NULL) break;
}
然后,代码会将答案关闭计算为预期值。在该修复之后,先前提出的重复适用。 Is floating point math broken?
area=11.775000 expected area=11.770000
perimeter=15.645596 expected perimeter=15.640000
代替比较FP数字以进行精确匹配,代码需要允许小容差。见comparing double values in C,
precision of comparing double values with EPSILON in C
可以进行各种简化和准确性改进。示例:
#include <assert.h>
/* calculate polygon perimeter */
double perimeter(double *vertices, int size) {
assert(size % 2 == 0 && size >= 0); // Insure only positive pairs are used
double perimeter = 0.0;
if (size > 1) {
double x_previous = vertices[size - 2];
double y_previous = vertices[size - 2 + 1];
while (size > 1) {
double x = *vertices++;
double y = *vertices++;
// hypot() certainly as accurate than sqrt(x*x + y*y) and avoids overflow
perimeter += hypot(x - x_previous, y - y_previous);
x_previous = x;
y_previous = y;
size -= 2;
}
}
return perimeter;
}