From 9b64b22806d4a274a7c879315101da6096ff7cf2 Mon Sep 17 00:00:00 2001 From: jun <83899451+zeichensystem@users.noreply.github.com> Date: Thu, 15 May 2025 09:21:30 +0200 Subject: [PATCH] Make signed guf_wrapping_mul more general Don't fail on mod == 0 (in weird cases where the given UNSIGNED_TYPE_MAX == SIGNED_TYPE_MAX) but res = mod > 0 ? (1u * res % mod) : res; --- src/guf_math_ckdint.h | 31 +++++++++++++------------------ tools/ckdint-gen.py | 5 ++--- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/guf_math_ckdint.h b/src/guf_math_ckdint.h index c7fb0cd..2f7f6da 100644 --- a/src/guf_math_ckdint.h +++ b/src/guf_math_ckdint.h @@ -68,6 +68,7 @@ cf. https://stackoverflow.com/questions/27001604/32-bit-unsigned-multiply-on-64-bit-causing-undefined-behavior (last-retrieved 2025-05-15) */ + #if defined(GUF_MATH_CKDINT_IMPL_STATIC) #define GUF_MATH_CKDINT_KWRDS static inline #else @@ -808,9 +809,8 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_int(int a, int b, int case GUF_MATH_CKD_OVERFLOW_NEG: { unsigned res = 1u * (unsigned)a * (unsigned)b; if (res > INT_MAX) { // This is the fix for implementation defined conversion from unsigned to signed. - const unsigned mod = (1u * (unsigned)INT_MAX + 1u); - GUF_ASSERT(mod > 0 && mod > INT_MAX); - res = 1u * res % mod; + const unsigned mod = (1u + (unsigned)INT_MAX); + res = mod > 0 ? (1u * res % mod) : res; *result = INT_MIN + (int)res; } else { *result = (int)res; @@ -940,9 +940,8 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_i8(int8_t a, int8_t b case GUF_MATH_CKD_OVERFLOW_NEG: { uint8_t res = 1u * (uint8_t)a * (uint8_t)b; if (res > INT8_MAX) { // This is the fix for implementation defined conversion from unsigned to signed. - const uint8_t mod = (1u * (uint8_t)INT8_MAX + 1u); - GUF_ASSERT(mod > 0 && mod > INT8_MAX); - res = 1u * res % mod; + const uint8_t mod = (1u + (uint8_t)INT8_MAX); + res = mod > 0 ? (1u * res % mod) : res; *result = INT8_MIN + (int8_t)res; } else { *result = (int8_t)res; @@ -1072,9 +1071,8 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_i16(int16_t a, int16_ case GUF_MATH_CKD_OVERFLOW_NEG: { uint16_t res = 1u * (uint16_t)a * (uint16_t)b; if (res > INT16_MAX) { // This is the fix for implementation defined conversion from unsigned to signed. - const uint16_t mod = (1u * (uint16_t)INT16_MAX + 1u); - GUF_ASSERT(mod > 0 && mod > INT16_MAX); - res = 1u * res % mod; + const uint16_t mod = (1u + (uint16_t)INT16_MAX); + res = mod > 0 ? (1u * res % mod) : res; *result = INT16_MIN + (int16_t)res; } else { *result = (int16_t)res; @@ -1204,9 +1202,8 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_i32(int32_t a, int32_ case GUF_MATH_CKD_OVERFLOW_NEG: { uint32_t res = 1u * (uint32_t)a * (uint32_t)b; if (res > INT32_MAX) { // This is the fix for implementation defined conversion from unsigned to signed. - const uint32_t mod = (1u * (uint32_t)INT32_MAX + 1u); - GUF_ASSERT(mod > 0 && mod > INT32_MAX); - res = 1u * res % mod; + const uint32_t mod = (1u + (uint32_t)INT32_MAX); + res = mod > 0 ? (1u * res % mod) : res; *result = INT32_MIN + (int32_t)res; } else { *result = (int32_t)res; @@ -1336,9 +1333,8 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_i64(int64_t a, int64_ case GUF_MATH_CKD_OVERFLOW_NEG: { uint64_t res = 1u * (uint64_t)a * (uint64_t)b; if (res > INT64_MAX) { // This is the fix for implementation defined conversion from unsigned to signed. - const uint64_t mod = (1u * (uint64_t)INT64_MAX + 1u); - GUF_ASSERT(mod > 0 && mod > INT64_MAX); - res = 1u * res % mod; + const uint64_t mod = (1u + (uint64_t)INT64_MAX); + res = mod > 0 ? (1u * res % mod) : res; *result = INT64_MIN + (int64_t)res; } else { *result = (int64_t)res; @@ -1468,9 +1464,8 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_ptrdiff_t(ptrdiff_t a case GUF_MATH_CKD_OVERFLOW_NEG: { size_t res = 1u * (size_t)a * (size_t)b; if (res > PTRDIFF_MAX) { // This is the fix for implementation defined conversion from unsigned to signed. - const size_t mod = (1u * (size_t)PTRDIFF_MAX + 1u); - GUF_ASSERT(mod > 0 && mod > PTRDIFF_MAX); - res = 1u * res % mod; + const size_t mod = (1u + (size_t)PTRDIFF_MAX); + res = mod > 0 ? (1u * res % mod) : res; *result = PTRDIFF_MIN + (ptrdiff_t)res; } else { *result = (ptrdiff_t)res; diff --git a/tools/ckdint-gen.py b/tools/ckdint-gen.py index 3355739..05a543f 100644 --- a/tools/ckdint-gen.py +++ b/tools/ckdint-gen.py @@ -232,9 +232,8 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> Tuple[str, s case GUF_MATH_CKD_OVERFLOW_NEG: {{ {uint_type} res = 1u * ({uint_type})a * ({uint_type})b; if (res > {int_max}) {{ // This is the fix for implementation defined conversion from unsigned to signed. - const {uint_type} mod = (1u * ({uint_type}){int_max} + 1u); - GUF_ASSERT(mod > 0 && mod > {int_max}); - res = 1u * res % mod; + const {uint_type} mod = (1u + ({uint_type}){int_max}); + res = mod > 0 ? (1u * res % mod) : res; *result = {int_min} + ({type})res; }} else {{ *result = ({type})res;