Skip to content

Instantly share code, notes, and snippets.

@dyeo
Created February 4, 2026 04:11
Show Gist options
  • Select an option

  • Save dyeo/0fc1970783744e5d12fe58c50cec0fbe to your computer and use it in GitHub Desktop.

Select an option

Save dyeo/0fc1970783744e5d12fe58c50cec0fbe to your computer and use it in GitHub Desktop.
/* arcalc.c
* Aspect Ratio Calculator
*
* Build:
* gcc -std=c90 -lm -pedantic -Wall -Wextra -Werror -O2 -o arcalc arcalc.c
*
* Usage:
* arcalc x y
* Reduce x:y to lowest terms.
*
* arcalc x1 y1 x2 y2
* Verify x1:y1 == x2:y2, or compute a single wildcard value (any non-number).
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <errno.h>
const double ARC_EPS = 1.e-12;
enum arc_errorc
{
ARC_FALSE = 0,
ARC_TRUE = 1,
#if EXIT_SUCCESS < EXIT_FAILURE
ARC_E_OK = EXIT_SUCCESS,
ARC_E_FAIL = EXIT_FAILURE,
#else
ARC_E_OK = EXIT_FAILURE,
ARC_E_FAIL = EXIT_SUCCESS,
#endif
ARC_E_BADARG
};
static void arc_helpf(const char *prog)
{
fprintf(stderr,
"Usage:\n"
" %s x y\n"
" Reduce x:y to lowest terms.\n"
" %s x1 y1 x2 y2\n"
" Verify x1:y1 == x2:y2, or compute a single wildcard value (any non-number).\n",
prog, prog);
}
static int arc_near(double a, double b)
{
double da = fabs(a), db = fabs(b);
double scl = da > db ? da : db;
if (scl < 1.0) scl = 1.0;
return fabs(a - b) <= (ARC_EPS * scl);
}
static int arc_same_pair(double a[2], double b[2])
{
return arc_near(a[0], b[0]) && arc_near(a[1], b[1]);
}
static int arc_is_zero(double v)
{
return v > -ARC_EPS && v < ARC_EPS;
}
static int arc_is_real(double v)
{
return !arc_is_zero(v) && (v == v) && v != HUGE_VAL && v != -HUGE_VAL;
}
static double arc_gcd(double a, double b)
{
double t;
a = fabs(a);
b = fabs(b);
if (arc_is_zero(a))
{
return b;
}
if (arc_is_zero(b))
{
return a;
}
while (b > ARC_EPS)
{
t = fmod(a, b);
a = b;
b = t;
if (b < ARC_EPS)
{
break;
}
}
return a;
}
static void arc_norm(double ar[2])
{
double g = arc_gcd(ar[0], ar[1]);
if (arc_is_real(g))
{
ar[0] /= g;
ar[1] /= g;
}
if (ar[1] < 0.0)
{
ar[0] = -ar[0];
ar[1] = -ar[1];
}
}
static int arc_getdouble(const char *s, double *out)
{
char *end = NULL;
double v;
int err;
errno = 0;
v = strtod(s, &end);
err = errno;
if (end == s || end == NULL || *end != '\0')
{
return ARC_FALSE;
}
if (err == ERANGE || v == HUGE_VAL || v == -HUGE_VAL)
{
return ARC_FALSE;
}
if (v != v)
{
return ARC_FALSE;
}
*out = v;
return ARC_TRUE;
}
static int arc_xmult(const char *prog, double ar[4], int w)
{
double lhs, rhs, scl, tol;
if (w < 0)
{
if (fabs(ar[0]) < ARC_EPS || fabs(ar[1]) < ARC_EPS || fabs(ar[2]) < ARC_EPS || fabs(ar[3]) < ARC_EPS)
{
int zi = arc_is_zero(ar[0]) ? 0 : arc_is_zero(ar[1]) ? 1
: arc_is_zero(ar[2]) ? 2
: 3;
fprintf(stderr, "ERROR: argument is zero (%d)\n", zi);
arc_helpf(prog);
return ARC_E_BADARG;
}
lhs = ar[0] * ar[3];
rhs = ar[1] * ar[2];
scl = fabs(lhs) > fabs(rhs) ? fabs(lhs) : fabs(rhs);
tol = ARC_EPS * scl;
if (fabs(lhs - rhs) > tol)
{
fprintf(stderr, "%g:%g != %g:%g\n", ar[0], ar[1], ar[2], ar[3]);
return ARC_E_FAIL;
}
return ARC_E_OK;
}
switch (w)
{
case 0:
if (fabs(ar[3]) < ARC_EPS)
{
fprintf(stderr, "ERROR: invalid argument (%d = %g)\n", 3, ar[3]);
arc_helpf(prog);
return ARC_E_BADARG;
}
ar[0] = (ar[2] * ar[1]) / ar[3];
break;
case 1:
if (fabs(ar[2]) < ARC_EPS)
{
fprintf(stderr, "ERROR: invalid argument (%d = %g)\n", 2, ar[2]);
arc_helpf(prog);
return ARC_E_BADARG;
}
ar[1] = (ar[0] * ar[3]) / ar[2];
break;
case 2:
if (fabs(ar[1]) < ARC_EPS)
{
fprintf(stderr, "ERROR: invalid argument (%d = %g)\n", 1, ar[1]);
arc_helpf(prog);
return ARC_E_BADARG;
}
ar[2] = (ar[0] * ar[3]) / ar[1];
break;
case 3:
if (fabs(ar[0]) < ARC_EPS)
{
fprintf(stderr, "ERROR: invalid argument (%d = %g)\n", 0, ar[0]);
arc_helpf(prog);
return ARC_E_BADARG;
}
ar[3] = (ar[2] * ar[1]) / ar[0];
break;
default:
fprintf(stderr, "ERROR: invalid wildcard position (%d)\n", w);
arc_helpf(prog);
return ARC_E_BADARG;
}
return ARC_E_OK;
}
int main(int argc, char **argv)
{
char *prog; /* program name */
int n; /* num. args */
int i; /* current index */
int w; /* wildcard index */
int r; /* result value */
double ar[4] = {0.0, 0.0, 0.0, 0.0}; /* aspect ratio(s) */
double nr[2] = {0.0, 0.0}; /* normalized ratio */
prog = argv[0] ? argv[0] : "arcalc";
n = argc - 1;
w = -1;
r = 0;
if (n != 2 && n != 4)
{
fprintf(stderr, "ERROR: incorrect number of arguments (%d)\n", n);
arc_helpf(prog);
return ARC_E_BADARG;
}
for (i = 0; i < n; ++i)
{
if (!arc_getdouble(argv[i + 1], &ar[i]))
{
if (w != -1)
{
fprintf(stderr, "ERROR: multiple wildcard arguments\n");
arc_helpf(prog);
return ARC_E_BADARG;
}
w = i;
}
}
for (i = 0; i < n; ++i)
{
if (i == w)
{
if (n == 2)
{
fprintf(stderr, "ERROR: argument is wildcard (%d); fraction is zero\n", i);
arc_helpf(prog);
return ARC_E_BADARG;
}
continue;
}
if (arc_is_zero(ar[i]))
{
fprintf(stderr, "ERROR: argument is zero (%d)\n", i);
arc_helpf(prog);
return ARC_E_BADARG;
}
}
if (n == 4)
{
r = arc_xmult(prog, ar, w);
if (r != ARC_E_OK)
{
return r;
}
}
nr[0] = ar[0];
nr[1] = ar[1];
arc_norm(nr);
if (n == 4)
{
if (!arc_same_pair(ar, nr) && !arc_same_pair(ar + 2, nr))
{
printf("%g:%g == %g:%g == %g:%g\n", ar[0], ar[1], ar[2], ar[3], nr[0], nr[1]);
}
else
{
printf("%g:%g == %g:%g\n", ar[0], ar[1], ar[2], ar[3]);
}
}
else
{
if (arc_same_pair(ar, nr))
{
printf("%g:%g\n", ar[0], ar[1]);
}
else
{
printf("%g:%g == %g:%g\n", ar[0], ar[1], nr[0], nr[1]);
}
}
return ARC_E_OK;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment