c++ 将取模运算符实现为溢出安全函数实现

l7wslrjt  于 2024-01-09  发布在  其他
关注(0)|答案(2)|浏览(179)

在C中,%运算符是一个余数运算符(a % b产生 a − trunc(a/B)·B),但我想实现一个模运算符(a − floor(a/B)·B),它是溢出安全的。
特别是,mod(LONG_MIN, -1)应该有定义的行为并产生0。(对于二进制补码,C
标准没有定义LONG_MIN % -1,因为它是通过引用除法定义的,LONG_MIN / −1溢出。)
例如,以下代码实现了函数,但它不是溢出安全的:

  1. long r = x % y;
  2. if ((x ^ y) < 0 && r != 0) {
  3. return r + y;
  4. }
  5. return r;

字符串
在C++中有没有一种有效的方法来实现这一点,而不需要特殊的溢出检查?或者只有在方法的开头添加一个溢出检查才能做到这一点?

wlp8pajw

wlp8pajw1#

当余数为负时,您所需要的就是(小心地)加上商值y
以下是C11解决方案:

  1. static inline long safe_modulus_long(long x, long y) {
  2. if (y == 0) {
  3. // division by zero: return a 0 modulus
  4. return 0;
  5. }
  6. if (x == LONG_MIN && y == -1) {
  7. // division overflow: the modulus is actually 0
  8. return 0;
  9. }
  10. long r = x % y;
  11. if (r < 0) {
  12. if (y > 0)
  13. r = y + r;
  14. else
  15. r = -(y - r);
  16. }
  17. return r;
  18. }

字符串
您可以使用其他类型特定的函数和泛型宏来支持所有标准整型:

  1. static inline long long safe_modulus_llong(long long x, long long y) {
  2. [...] // same implementation as above using LLONG_MIN
  3. }
  4. static inline int safe_modulus_int(int x, int y) {
  5. [...] // same implementation as above using INT_MIN
  6. }
  7. static inline unsigned long long safe_modulus_ullong(unsigned long long x, unsigned long long y) {
  8. return y ? x % y : 0;
  9. }
  10. static inline unsigned long safe_modulus_ulong(unsigned long x, unsigned long y) {
  11. return y ? x % y : 0;
  12. }
  13. static inline unsigned int safe_modulus_long(unsigned int x, unsigned int y) {
  14. return y ? x % y : 0;
  15. }
  16. #define SAFE_MUDULUS(x, y) _Generic((x) % (y), \
  17. unsigned long long int: safe_modulus_ullong(x, y), \
  18. long long int: safe_modulus_llong(x, y), \
  19. unsigned long int: safe_modulus_ulong(x, y), \
  20. long int: safe_modulus_long(x, y), \
  21. unsigned int: safe_modulus_uint(x, y), \
  22. int: safe_modulus_int(x, y))


对于C++,你可以使用重载来实现所有整型的safe_modulus

  1. static inline long long safe_modulus(long long x, long long y) {
  2. if (y == 0) {
  3. // division by zero: return a 0 modulus
  4. return 0;
  5. }
  6. if (x == LLONG_MIN && y == -1) {
  7. // division overflow: the modulus is actually 0
  8. return 0;
  9. }
  10. long long r = x % y;
  11. if (r < 0) {
  12. if (y > 0)
  13. r = y + r;
  14. else
  15. r = -(y - r);
  16. }
  17. return r;
  18. }
  19. static inline long safe_modulus(long x, long y) {
  20. if (y == 0) {
  21. // division by zero: return a 0 modulus
  22. return 0;
  23. }
  24. if (x == LONG_MIN && y == -1) {
  25. // division overflow: the modulus is actually 0
  26. return 0;
  27. }
  28. long r = x % y;
  29. if (r < 0) {
  30. if (y > 0)
  31. r = y + r;
  32. else
  33. r = -(y - r);
  34. }
  35. return r;
  36. }
  37. static inline int safe_modulus(int x, int y) {
  38. if (y == 0) {
  39. // division by zero: return a 0 modulus
  40. return 0;
  41. }
  42. if (x == INT_MIN && y == -1) {
  43. // division overflow: the modulus is actually 0
  44. return 0;
  45. }
  46. int r = x % y;
  47. if (r < 0) {
  48. if (y > 0)
  49. r = y + r;
  50. else
  51. r = -(y - r);
  52. }
  53. return r;
  54. }
  55. static inline unsigned long long safe_modulus(unsigned long long x, unsigned long long y) {
  56. return y ? x % y : 0;
  57. }
  58. static inline unsigned long safe_modulus(unsigned long x, unsigned long y) {
  59. return y ? x % y : 0;
  60. }
  61. static inline unsigned int safe_modulus(unsigned int x, unsigned int y) {
  62. return y ? x % y : 0;
  63. }

展开查看全部
vkc1a9a2

vkc1a9a22#

只需为边界情况添加额外的检查:

  1. long safe_mod(long x, long y)
  2. {
  3. if (x == LONG_MIN && y == -1) {
  4. return 0;
  5. } else if (x > LONG_MIN && x < 0 && y == LONG_MIN) {
  6. return -x;
  7. } else {
  8. long r = x % y;
  9. if ((x ^ y) < 0 && r != 0) {
  10. return r + y;
  11. }
  12. return r;
  13. }

字符串

相关问题