Skip to content

Instantly share code, notes, and snippets.

@imaami
Last active June 10, 2026 21:30
Show Gist options
  • Select an option

  • Save imaami/ca7aaf2e0748ce363a9650e5d5af6f7c to your computer and use it in GitHub Desktop.

Select an option

Save imaami/ca7aaf2e0748ce363a9650e5d5af6f7c to your computer and use it in GitHub Desktop.
Faithfully replicating a bad way to do rounding
/*
* gcc -O2 -march=native -mtune=native -flto=auto rounding.c -o rounding -lm
*/
#define _GNU_SOURCE
#include <ctype.h>
#include <errno.h>
#include <fenv.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
/**
* @brief Round `d` to approximately `n` decimal places (as close to
* the correct result as possible with IEEE754 floating-point)
*
* The result is equal to the result of parsing `snprintf()`'s output
* with `atof()`, with equivalent rounding done in the format string.
*/
#define round_to_precision(n, d) _Generic( \
&(char[((n) < 1) + ((n) > 0) * (n)]){0} , char(*)[ 1]:round1 \
, char(*)[ 2]:round2 , char(*)[ 3]:round3 , char(*)[ 4]:round4 \
, char(*)[ 5]:round5 , char(*)[ 6]:round6 , char(*)[ 7]:round7 \
, char(*)[ 8]:round8 , char(*)[ 9]:round9 , char(*)[10]:round10 \
, char(*)[11]:round11 , char(*)[12]:round12 , char(*)[13]:round13 \
, char(*)[14]:round14 , char(*)[15]:round15 , char(*)[16]:round16 \
, char(*)[17]:round17 , char(*)[18]:round18 , char(*)[19]:round19 \
, char(*)[20]:round20 , char(*)[21]:round21 , char(*)[22]:round22)(d)
#define precision_mul(n) _Generic( \
&(char[((n) < 1) + ((n) > 0) * (n)]){0} \
, char(*)[ 1]: 10.0L \
, char(*)[ 2]: 100.0L \
, char(*)[ 3]: 1000.0L \
, char(*)[ 4]: 10000.0L \
, char(*)[ 5]: 100000.0L \
, char(*)[ 6]: 1000000.0L \
, char(*)[ 7]: 10000000.0L \
, char(*)[ 8]: 100000000.0L \
, char(*)[ 9]: 1000000000.0L \
, char(*)[10]: 10000000000.0L \
, char(*)[11]: 100000000000.0L \
, char(*)[12]: 1000000000000.0L \
, char(*)[13]: 10000000000000.0L \
, char(*)[14]: 100000000000000.0L \
, char(*)[15]: 1000000000000000.0L \
, char(*)[16]: 10000000000000000.0L \
, char(*)[17]: 100000000000000000.0L \
, char(*)[18]: 1000000000000000000.0L \
, char(*)[19]: 10000000000000000000.0L \
, char(*)[20]: 100000000000000000000.0L \
, char(*)[21]: 1000000000000000000000.0L \
, char(*)[22]:10000000000000000000000.0L)
#define define_round_function(p) \
__attribute__((always_inline)) \
static inline double \
round##p (double d) { \
const long double d_ = (long double)d; \
const long double t = truncl(d_); \
return (double)(nearbyintl(precision_mul(p) \
* (d_ - t)) / precision_mul(p) + t); \
} _Static_assert(1, "")
define_round_function(22);
define_round_function(21);
define_round_function(20);
define_round_function(19);
define_round_function(18);
define_round_function(17);
define_round_function(16);
define_round_function(15);
define_round_function(14);
define_round_function(13);
define_round_function(12);
define_round_function(11);
define_round_function(10);
define_round_function( 9);
define_round_function( 8);
define_round_function( 7);
define_round_function( 6);
define_round_function( 5);
define_round_function( 4);
define_round_function( 3);
define_round_function( 2);
define_round_function( 1);
#undef define_round_function
#undef precision_mul
#define TEST(n, d, b, t, f) do { \
if (0 < snprintf(b, sizeof b, "%." #n "lf", d)) \
{ \
double p = atof(b); \
double q = round_to_precision(n, d); \
printf("%27.22lf (printf + atof)\n" \
"%27.22lf (round" #n ")\n", p, q); \
int failure = p != q; \
if (failure) \
fputs("FAIL: round" #n "\n", stderr); \
f += failure; \
t += 1; \
} \
} while (0)
int
main (int c,
char **v)
{
(void)fesetround(FE_TONEAREST);
int failed = 0;
int tested = 0;
for (int i = 0; ++i < c;) {
errno = 0;
char *e = v[i];
long long n = strtoll(v[i], &e, 0);
if (errno || e == v[i] || (*e && !isspace(*e)))
continue;
const double d = n ? (double)(1000.0L / (long double)n) : 1000.0;
char buf[256];
TEST(22, d, buf, tested, failed);
TEST(21, d, buf, tested, failed);
TEST(20, d, buf, tested, failed);
TEST(19, d, buf, tested, failed);
TEST(18, d, buf, tested, failed);
TEST(17, d, buf, tested, failed);
TEST(16, d, buf, tested, failed);
TEST(15, d, buf, tested, failed);
TEST(14, d, buf, tested, failed);
TEST(13, d, buf, tested, failed);
TEST(12, d, buf, tested, failed);
TEST(11, d, buf, tested, failed);
TEST(10, d, buf, tested, failed);
TEST( 9, d, buf, tested, failed);
TEST( 8, d, buf, tested, failed);
TEST( 7, d, buf, tested, failed);
TEST( 6, d, buf, tested, failed);
TEST( 5, d, buf, tested, failed);
TEST( 4, d, buf, tested, failed);
TEST( 3, d, buf, tested, failed);
TEST( 2, d, buf, tested, failed);
TEST( 1, d, buf, tested, failed);
}
if (tested)
printf("passed %d of %d tests\n",
tested - failed, tested);
return tested && failed;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment