SilverSight/c/q16_canonical.c
allaun fec200205e feat(rrc): port ReceiptDensity, PolyFactorIdentity, EntropyCandidates and add Q16_16 roundtrip test
Ports three Research Stack RRC gates into formal/SilverSight/RRC/ using the existing CoreFormalism braid and Q16_16 surfaces.

Adds a Lean ↔ C ↔ Python Q16_16 roundtrip test:

  - c/q16_canonical.c: canonical saturating Q16_16 C library

  - exe/Q16_16Roundtrip.lean: Lake extern_lib linked executable

  - tests/test_q16_roundtrip.py: Python ↔ C harness (revived from quarantine)

Updates lakefile.lean with q16-roundtrip executable and extern_lib q16_canonical.

Build: lake build SilverSightRRC 3006 jobs, 0 errors; q16-roundtrip 5949 jobs, 0 errors
2026-06-21 10:39:18 -05:00

77 lines
2.3 KiB
C

/* Canonical Q16_16 fixed-point arithmetic (C implementation).
*
* This is the C side of the Lean <-> C <-> Python roundtrip test.
* It mirrors python/q16_canonical.py:
* - banker's rounding for float->fixed conversion
* - saturation to INT32_MIN/INT32_MAX
* - exact integer conversions
* - saturating add/sub
* - mul/div with banker's rounding (double-based, matching Python)
*/
#include <math.h>
#include <stdint.h>
#include <float.h>
#define Q16_SCALE 65536.0
#define Q16_SCALE_INT 65536
#define Q16_MIN_RAW (-2147483648LL)
#define Q16_MAX_RAW 2147483647LL
static int64_t clamp_int64(long long v) {
if (v < Q16_MIN_RAW) return Q16_MIN_RAW;
if (v > Q16_MAX_RAW) return Q16_MAX_RAW;
return v;
}
/* Convert float to Q16_16 raw value using banker's rounding. */
int32_t float_to_q16_nearbyint(double f) {
if (isnan(f) || isinf(f)) {
return 0; /* Python raises; C returns a sentinel. */
}
long long r = (long long)nearbyint(f * Q16_SCALE);
return (int32_t)clamp_int64(r);
}
/* Convert Q16_16 raw value to float (exact). */
double q16_to_float(int32_t q) {
return (double)q / Q16_SCALE;
}
/* Convert integer to Q16_16 raw value (exact, with saturation). */
int32_t int_to_q16(int32_t i) {
long long r = (long long)i * Q16_SCALE_INT;
return (int32_t)clamp_int64(r);
}
/* Convert Q16_16 raw value to integer, truncating toward zero. */
int32_t q16_to_int(int32_t q) {
return q / Q16_SCALE_INT;
}
/* Saturating addition of two Q16_16 values. */
int32_t q16_add(int32_t a, int32_t b) {
long long r = (long long)a + (long long)b;
return (int32_t)clamp_int64(r);
}
/* Saturating subtraction of two Q16_16 values. */
int32_t q16_sub(int32_t a, int32_t b) {
long long r = (long long)a - (long long)b;
return (int32_t)clamp_int64(r);
}
/* Multiply two Q16_16 values with banker's rounding (matches Python). */
int32_t q16_mul(int32_t a, int32_t b) {
double prod = (double)a * (double)b;
long long r = (long long)nearbyint(prod / Q16_SCALE);
return (int32_t)clamp_int64(r);
}
/* Divide two Q16_16 values with banker's rounding. */
int32_t q16_div(int32_t a, int32_t b) {
if (b == 0) return 0;
double num = (double)a * Q16_SCALE;
long long r = (long long)nearbyint(num / (double)b);
return (int32_t)clamp_int64(r);
}