0

I have a program to calculate a poylgn área and perimeter, the program receives a text file with coordinates and calculate the area.

Im with some issues on the calculations. Now Im trying to compare doubles and I dont understand why its not working.

I have a text file with 3 lines:

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

And I expect this results:

    double expectedarea = 11.77;     
    double expectedperimeter = 15.64;     
    double expectedarea1 = 18.10;     
    double expectedperimeter1 = 17.02;     
    double expectedarea2 = 21.33;     
    double expectedperimeter2 = 16.60;  

So I would expected that the message "Arrays are the same" appear for the 3 cases because Im giving the correct value, but Im getting the message arrays are different for the 3 cases.

Do you understand why I get always the message arrays are different?

#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);
}
Chen
  • 37
  • 3
  • 2
    Debugging tip: print the delta (`fabs(polygon_area - expectedarea)` and `fabs(perim - expectedperimeter)`) to your `"Arrays are the different"` message. Then you can see if it is a rounding error or a real difference. Generally, you should compare floatingpoint numbers with some allowed difference to account for rounding errors. – grek40 Dec 15 '16 at 11:31
  • Now would be a good time to start learning how to use debugger. It helps you to follow calculation results as you step through the program. – user694733 Dec 15 '16 at 11:32
  • Thanks for your answer. I alreay did that and even when the rouding its icual it appears the message "Arrays diferente". – Chen Dec 15 '16 at 11:34
  • Had to edit my comment a bit from `abs` to `fabs` since it doesn't make sense to reduce the doubles to integers first. – grek40 Dec 15 '16 at 11:35
  • try doing [this](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) – Travis Dec 15 '16 at 23:08

1 Answers1

1

In testing OP's code, the list, as posted, had a space after the 0.7. Normally this white-space is not an issue, yet my saving of the file text.txt caused the line to end with " \r\n" and the '\r' created additional tokens. Additional white-space characters to strtok(NULL, " \n\r\t") solved this.

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

Depending on how the list is saved, the last line may or may not end with a '\n'. This becomes an issue with OP's method of lopping off the '\n' with the below as it may render the last line as "1.7 4.9 5.1 5.8 7.0 2.8 4.8 0.1 1.5 1." (last 4 missing).

    line[strlen(line) - 1] = 0;

Better to use the below which does not require a final '\n' to work right.

    line[strcspn(line, "\n")] = '\0';

OP's code is creating one too many coordinates in its xy list. It also has trouble potential due to other white-spaces. A more complete list is added.

while (token != NULL) {
  xy = atof(token);
  polygon_vertices[xycount++] = xy;
  token = strtok(NULL, " \n\r\t");  // more white-spaces.
  if (token == NULL) break;
}

Code will then calculate answer close to the expected values. After that fix, an earlier proposed duplicate applies. Is floating point math broken?

   area=11.775000   expected area=11.770000
   perimeter=15.645596  expected perimeter=15.640000

Rather than compare FP numbers for exact matching, code need to allow for a small tolerance. See comparing double values in C,
precision of comparing double values with EPSILON in C.


Various simplifications and accuracy improvements possible. Example:

#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;
}
Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256