From 8781656616e0183d1d92c758a210adb96558137b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Kl=C3=A9ger?= Date: Mon, 13 Oct 2025 14:24:56 +0200 Subject: [PATCH] hw-01: Fix floating point imprecision --- homework-01-two-circles/main.c | 166 ++++++++++++++++++++++----------- 1 file changed, 109 insertions(+), 57 deletions(-) diff --git a/homework-01-two-circles/main.c b/homework-01-two-circles/main.c index fee4a52..0492238 100644 --- a/homework-01-two-circles/main.c +++ b/homework-01-two-circles/main.c @@ -1,86 +1,138 @@ #include -#include -#include +#include #include #include +#include +#include #ifdef __PROGTEST__ #define debug(...) ((void)0) #else -#define debug(fmt, ...) fprintf(stdout, "[%s:%d %s()] " fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__) +#define debug(fmt, ...) \ + fprintf(stdout, "[%s:%d %s()] " fmt "\n", __FILE__, __LINE__, __func__, \ + ##__VA_ARGS__) #endif +double clamp(double d) { + if (d > 1.0) { + return 1.0; + } else if (d < -1.0) { + return -1.0; + } else { + return d; + } +} + // https://www.geeksforgeeks.org/dsa/area-of-intersection-of-two-circles/ double intersection_area(double a_r, double b_r, double distance) { - double a_square = a_r * a_r; - double b_square = b_r * b_r; - double distane_square = distance * distance; + double a_square = a_r * a_r; + double b_square = b_r * b_r; + double distance_square = distance * distance; - double alpha = acos((distane_square + a_square - b_square) / (2 * a_r * distance)) * 2; - double beta = acos((distane_square + b_square - a_square) / (2 * b_r * distance)) * 2; + double alpha_acos = + (distance_square + a_square - b_square) / (2 * a_r * distance); + double beta_acos = + (distance_square + b_square - a_square) / (2 * b_r * distance); - double a1 = 0.5 * beta * b_square - 0.5 * b_square * sin(beta); - double a2 = 0.5 * alpha * a_square - 0.5 * a_square * sin(alpha); + double alpha = acos(clamp(alpha_acos)) * 2; + double beta = acos(clamp(beta_acos)) * 2; - double area = a1 + a2; + double a1 = 0.5 * beta * b_square - 0.5 * b_square * sin(beta); + double a2 = 0.5 * alpha * a_square - 0.5 * a_square * sin(alpha); - debug("Area = %lf", area); + double area = a1 + a2; - return area; + debug("Area = %lf", area); + + return area; } -double circle_area(double radius) { - return M_PI * radius * radius; -} +double circle_area(double radius) { return M_PI * radius * radius; } int main() { - double a_center_x = 0, a_center_y = 0, a_radius = 0, b_center_x = 0, b_center_y = 0, b_radius = 0; + double a_center_x = 0, a_center_y = 0, a_radius = 0, b_center_x = 0, + b_center_y = 0, b_radius = 0; - printf("Zadejte parametry kruznice #1:\n"); + printf("Zadejte parametry kruznice #1:\n"); - int inputs_read = scanf("%lf %lf %lf", &a_center_x, &a_center_y, &a_radius); - if(inputs_read != 3 || a_radius <= 0) { - printf("Nespravny vstup.\n"); - return EXIT_FAILURE; - } + int inputs_read = scanf("%lf %lf %lf", &a_center_x, &a_center_y, &a_radius); + if (inputs_read != 3 || a_radius <= 0) { + printf("Nespravny vstup.\n"); + return EXIT_FAILURE; + } - printf("Zadejte parametry kruznice #2:\n"); + printf("Zadejte parametry kruznice #2:\n"); - inputs_read = scanf("%lf %lf %lf", &b_center_x, &b_center_y, &b_radius); - if(inputs_read != 3 || b_radius <= 0) { - printf("Nespravny vstup.\n"); - return EXIT_FAILURE; - } + inputs_read = scanf("%lf %lf %lf", &b_center_x, &b_center_y, &b_radius); + if (inputs_read != 3 || b_radius <= 0) { + printf("Nespravny vstup.\n"); + return EXIT_FAILURE; + } + double center_distance = + sqrt((b_center_x - a_center_x) * (b_center_x - a_center_x) + + (b_center_y - a_center_y) * (b_center_y - a_center_y)); - double center_distance = sqrt((b_center_x - a_center_x) * (b_center_x - a_center_x) + (b_center_y - a_center_y) * (b_center_y - a_center_y)); + debug("Distance of centers = %0.60lf", center_distance); + debug("Radii difference = %0.60lf", a_radius - b_radius); + debug("Radii difference = %0.60lf", b_radius - a_radius); + debug("Center + #1 = %0.60lf", a_center_x + a_radius); + debug("Center + #2 = %0.60lf", b_center_x + b_radius); - double radii_sum = a_radius + b_radius; - if(a_center_x == b_center_x && a_center_y == b_center_y && a_radius == b_radius) { - printf("Kruznice splyvaji, prekryv: %lf\n", circle_area(a_radius)); - } else if(center_distance <= a_radius - b_radius) { - debug("B radius = %0.60lf", b_radius); - debug("Double A radius = %0.60lf", a_radius * 2); - if(center_distance + b_radius == a_radius) { - printf("Vnitrni dotyk, kruznice #2 lezi uvnitr kruznice #1, prekryv: %lf\n", circle_area(b_radius)); - } else { - printf("Kruznice #2 lezi uvnitr kruznice #1, prekryv: %lf\n", circle_area(b_radius)); - } - } else if(center_distance <= b_radius - a_radius) { - debug("A radius = %0.60lf", a_radius); - debug("Double B radius = %0.60lf", b_radius * 2); - if(center_distance + a_radius == b_radius) { - printf("Vnitrni dotyk, kruznice #1 lezi uvnitr kruznice #2, prekryv: %lf\n", circle_area(a_radius)); - } else { - printf("Kruznice #1 lezi uvnitr kruznice #2, prekryv: %lf\n", circle_area(a_radius)); - } - } else if(center_distance < radii_sum) { - printf("Kruznice se protinaji, prekryv: %lf\n", intersection_area(a_radius, b_radius, center_distance)); - } else if(center_distance == radii_sum) { - printf("Vnejsi dotyk, zadny prekryv.\n"); - } else { - printf("Kruznice lezi vne sebe, zadny prekryv.\n"); - } + double radii_sum = a_radius + b_radius; + double radii_difference = fabs(a_radius - b_radius); + double epsilon = 100 * DBL_EPSILON * fmax(a_radius, b_radius); - return EXIT_SUCCESS; + // Both origin points and radii are identical + bool are_identical = a_center_x == b_center_x && a_center_y == b_center_y && a_radius == b_radius; + + // d = r1 + r2 + // d - r1 - r1 < epsilon + bool are_touching_from_outside = fabs(center_distance - radii_sum) < epsilon; + + // d = |r1 - r2| + // d - |r1 - r2| < epsilon + bool are_touching_from_inside = fabs(center_distance - radii_difference) < epsilon; + + // d < |r1 - r2| + bool is_fully_inside = center_distance < radii_difference; + + // d < r1 + r2 + bool are_overlapping = center_distance < radii_sum; + + if (are_identical) { + printf("Kruznice splyvaji, prekryv: %lf\n", circle_area(a_radius)); + } + else if (are_touching_from_inside) { + if (a_radius > b_radius) { + printf( + "Vnitrni dotyk, kruznice #2 lezi uvnitr kruznice #1, prekryv: %lf\n", + circle_area(b_radius)); + } else { + printf( + "Vnitrni dotyk, kruznice #1 lezi uvnitr kruznice #2, prekryv: %lf\n", + circle_area(a_radius)); + } + } + else if (are_touching_from_outside) { + printf("Vnejsi dotyk, zadny prekryv.\n"); + } + else if (is_fully_inside) { + if (a_radius > b_radius) { + printf("Kruznice #2 lezi uvnitr kruznice #1, prekryv: %lf\n", + circle_area(b_radius)); + } else { + printf("Kruznice #1 lezi uvnitr kruznice #2, prekryv: %lf\n", + circle_area(a_radius)); + } + } + else if (are_overlapping) { + printf("Kruznice se protinaji, prekryv: %lf\n", + intersection_area(a_radius, b_radius, center_distance)); + } + else { + printf("Kruznice lezi vne sebe, zadny prekryv.\n"); + } + + return EXIT_SUCCESS; }