If this code is written correctly, it will block undefined behavior from happening in the case of signed overflow (guaranteeing that the program halts instead), and also guarantee that the program halts in the case of unsigned overflow/wraparound.
//overflow.h
#define err exit(1)
//overflow blocking code based on details at https://cplusplus.com/articles/DE18T05o/
#include <inttypes.h>
#define adr size_t
#define long int64_t
#define ulong uint64_t
const long LZ = ((long)0);
long divL(long v, long b)
{
if (b == LZ)
err;
if ((v == INT64_MIN) && (b == ((long)(-1))))
err;
return (long)(v / b);
}
ulong usdivL(ulong v, ulong b)
{
if (b == ((ulong)0))
err;
return (ulong)(v / b);
}
adr u_as_adr(ulong n)
{
if (n > SIZE_MAX)
err;
return (adr)n;
}
#ifdef __GNUC__
long add(long a, long b)
{
long r = 0;
if (__builtin_add_overflow(a, b, &r))
err;
return r;
}
long sub(long a, long b)
{
long r = 0;
if (__builtin_sub_overflow(a, b, &r))
err;
return r;
}
long mul(long a, long b)
{
long r = 0;
if (__builtin_mul_overflow(a, b, &r))
err;
return r;
}
adr uadd(adr a, adr b)
{
adr r = 0;
if (__builtin_add_overflow(a, b, &r))
err;
return r;
}
adr usub(adr a, adr b)
{
adr r = 0;
if (__builtin_sub_overflow(a, b, &r))
err;
return r;
}
adr umul(adr a, adr b)
{
adr r = 0;
if (__builtin_mul_overflow(a, b, &r))
err;
return r;
}
ulong usadd(ulong a, ulong b)
{
ulong r = 0;
if (__builtin_add_overflow(a, b, &r))
err;
return r;
}
ulong ussub(ulong a, ulong b)
{
ulong r = 0;
if (__builtin_sub_overflow(a, b, &r))
err;
return r;
}
ulong usmul(ulong a, ulong b)
{
ulong r = 0;
if (__builtin_mul_overflow(a, b, &r))
err;
return r;
}
#else
ulong usadd(ulong a, ulong b)
{
ulong n = a + b;// C standard guarantees wraparound here
if (n < a)
err;
return n;
}
ulong ussub(ulong a, ulong b)
{
if (b > a)
err;
return a - b;
}
ulong usmul(ulong a, ulong b)
{if(b==0)return 0;
if (a > (usdivL(UINT64_MAX, b)))
err;
return a * b;
}
long inv(long n)
{//inverting a number means subtracting it from zero
if (n == LZ)
return n;
if (n == INT64_MIN)
err;
return LZ - n;
}
long clamp(ulong n)
{
if (n > ((ulong)(INT64_MAX)))
err;
return (long)n;
}
long mul(long a, long b)
{ //signed multiplication
if ((b == LZ) || (a == LZ))
return LZ;
if (b == ((long)1))
return a;
if (a == ((long)1))
return b;
long an = 0;
long bn = 0;
if (a < LZ)
an = inv(a);
else
an = a;
if (b < LZ)
bn = inv(b);
else
bn = b;
ulong r = usmul((ulong)an, (ulong)bn);
if ((a < LZ) != (b < LZ))
{
if (r == (((ulong)(INT64_MAX)) + ((ulong)1)))
return INT64_MIN;
return inv(clamp(r));
}
return clamp(r);
}
adr uadd(adr a, adr b) { return u_as_adr(usadd((ulong)a, (ulong)b)); }
adr usub(adr a, adr b) { return u_as_adr(ussub((ulong)a, (ulong)b)); }
adr umul(adr a, adr b) { return u_as_adr(usmul((ulong)a, (ulong)b)); }
long sub(long a, long b)
{ //signed subtraction
if (b == LZ)
return a;
if (a == LZ)
return inv(b);
if (a > LZ)
{
if (b > LZ)
{
return a - b;
}
else
{//(a)-(-b)=a+b
return clamp(usadd((ulong)(a), (ulong)(inv(b))));
}
}
else
{
if (b > LZ)
{//(-a)-(b)=-(a+b)
return inv(clamp(usadd((ulong)(inv(a)), (ulong)(b))));
}
else
{//(-a)-(-b)=b-a=b+(-a)
return a - b;
}
}
}
long add(long a, long b)
{
if (a == LZ)
return b;
if (b == LZ)
return a;
if ((a > LZ) != (b > LZ))//overflow can't happen if signs are opposite
return a + b;
if (a == INT64_MIN)//can't invert the minimum
return sub(a, inv(b));//a+b=b+a=a-(-b)
return sub(b, inv(a));
}
#endif
#define err exit(1)
haven't seen something like that. do people really do it? \$\endgroup\$#define
in this code needs to go. \$\endgroup\$typedef
) will do. (These macros are even worse because they intend to replace keywords) \$\endgroup\$