/* 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 #include #include #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); }