On 12/5/20 1:42 PM, Bruno Haible wrote:
How about adding a macro

   #define SAFE_INT_MULTIPLY(a, b, result) \
     ! INT_MULTIPLY_WRAPV (a, b, result)

and documenting it as a safe way to do integer multiplication, regardless of
compiler options in effect?

Sure, we can do that. I prefer the name INT_MULTIPLY_OK though, as it's shorter, it fits better with intprops.h's naming conventions, and "SAFE" is a little misleading (the macro is not safe when, for example, its last argument is a null pointer). I installed the attached patch to do that; if you're not a fan we can always revert it.

Your reaction to the *_WRAPV names just goes to show how bad I am with 
marketing....
>From c3545bdddacc4974fff87f3f2632c03e96ebbf05 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Sat, 5 Dec 2020 15:54:22 -0800
Subject: [PATCH] intprops: Add INT_ADD_OK etc.

* doc/intprops.texi (Checking Integer Overflow): New section.
* lib/intprops.h: From a suggestion by Bruno Haible in:
https://lists.gnu.org/r/bug-gnulib/2020-12/msg00051.html
(SAFE_INT_ADD, SAFE_INT_SUBTRACT, SAFE_INT_MULTIPLY): New macros.
---
 ChangeLog         |   6 +++
 doc/intprops.texi | 109 +++++++++++++++++++++++++++++++++++++++++++++-
 lib/intprops.h    |  29 ++++++++++++
 3 files changed, 143 insertions(+), 1 deletion(-)

diff --git a/ChangeLog b/ChangeLog
index 8db9975f5..72895ac78 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2020-12-05  Paul Eggert  <egg...@cs.ucla.edu>
 
+	intprops: Add INT_ADD_OK etc.
+	* doc/intprops.texi (Checking Integer Overflow): New section.
+	* lib/intprops.h: From a suggestion by Bruno Haible in:
+	https://lists.gnu.org/r/bug-gnulib/2020-12/msg00051.html
+	(SAFE_INT_ADD, SAFE_INT_SUBTRACT, SAFE_INT_MULTIPLY): New macros.
+
 	doc: move exotic platfroms to Target Platforms
 	* doc/gnulib-intro.texi (Supported Platforms)
 	(Formerly Supported Platforms, Unsupported Platforms):
diff --git a/doc/intprops.texi b/doc/intprops.texi
index 294c235f5..f3a958a75 100644
--- a/doc/intprops.texi
+++ b/doc/intprops.texi
@@ -49,7 +49,8 @@ but it does not assume that signed integer arithmetic wraps around.
 @menu
 * Arithmetic Type Properties::  Determining properties of arithmetic types.
 * Integer Bounds::              Bounds on integer values and representations.
-* Wraparound Arithmetic::       Well-defined behavior on signed overflow.
+* Checking Integer Overflow::   Checking for overflow while computing integers.
+* Wraparound Arithmetic::       Well-defined behavior on integer overflow.
 * Integer Type Overflow::       General integer overflow checking.
 * Integer Range Overflow::      Integer overflow checking if bounds are known.
 @end menu
@@ -150,6 +151,107 @@ in_off_t_range (intmax_t a)
 @}
 @end example
 
+@node Checking Integer Overflow
+@subsection Checking Integer Overflow
+
+@cindex integer overflow checking
+
+Signed integer arithmetic has undefined behavior on overflow in C@.
+Although almost all modern computers use two's complement signed
+arithmetic that is well-defined to wrap around, C compilers routinely
+optimize assuming that signed integer overflow cannot occur, which
+means that a C program cannot easily get at the underlying machine
+arithmetic.  For example:
+
+@example
+if ((a + b < b) == (a < 0))
+  a += b;
+else
+  print ("overflow");
+@end example
+
+@noindent
+might not work as expected if @code{a} and @code{b} are signed,
+because a compiler can assume that signed overflow cannot occur and
+treat the entire @code{if} expression as if it were true.  And even if
+@code{a} is unsigned, the expression might not work as expected if
+@code{b} is negative or is wider than @code{a}.
+
+The following macros work around this problem by returning an overflow
+indication while computing the sum, difference, or product of two
+integers.  For example, if @code{i} is of type @code{int},
+@code{INT_ADD_OK (INT_MAX - 1, 1, &i)} sets @code{i} to
+@code{INT_MAX} and returns true, whereas @code{INT_ADD_OK (INT_MAX, 1,
+&i)} returns false.
+
+Example usage:
+
+@example
+#include <intprops.h>
+#include <stdio.h>
+
+/* Compute A * B, reporting whether overflow occurred.  */
+void
+print_product (long int a, long int b)
+@{
+  long int r;
+  if (INT_MULTIPLY_OK (a, b, r))
+    printf ("result is %ld\n", r);
+  else
+    printf ("overflow\n");
+@}
+@end example
+
+These macros work for both signed and unsigned integers, so they can
+be used with integer types like @code{time_t} that may or may not be
+signed, depending on the platform.
+
+These macros have the following restrictions:
+
+@itemize @bullet
+@item
+Their first two arguments must be integer expressions.
+
+@item
+Their last argument must be a non-null pointer to an integer.
+
+@item
+They may evaluate their arguments zero or multiple times, so the
+arguments should not have side effects.
+
+@item
+They are not necessarily constant expressions, even if all their
+arguments are constant expressions.
+@end itemize
+
+@table @code
+@item INT_ADD_OK (@var{a}, @var{b}, @var{r})
+@findex INT_ADD_OK
+Compute the sum of @var{a} and @var{b}.  If it fits into
+@code{*@var{r}}, store it there and return true.  Otherwise return
+false, possibly modifying @code{*@var{r}} to an unspecified value.
+See above for restrictions.
+
+@item INT_SUBTRACT_OK (@var{a}, @var{b}, @var{r})
+@findex INT_SUBTRACT_OK
+Compute the difference between @var{a} and @var{b}.  If it fits into
+@code{*@var{r}}, store it there and return true.  Otherwise return
+false, possibly modifying @code{*@var{r}} to an unspecified value.
+See above for restrictions.
+
+@item INT_MULTIPLY_OK (@var{a}, @var{b}, @var{r})
+@findex INT_MULTIPLY_OK
+Compute the product of @var{a} and @var{b}.  If it fits into
+@code{*@var{r}}, store it there and return true.  Otherwise return
+false, possibly modifying @code{*@var{r}} to an unspecified value.
+See above for restrictions.
+@end table
+
+Other macros are available if you need wrapped-around results when
+overflow occurs (@pxref{Wraparound Arithmetic}), or if you need to
+check for overflow in operations other than addition, subtraction, and
+multiplication (@pxref{Integer Type Overflow}).
+
 @node Wraparound Arithmetic
 @subsection Wraparound Arithmetic with Integers
 
@@ -238,6 +340,11 @@ low-order bits are the mathematically-correct product.  See above for
 restrictions.
 @end table
 
+Other macros are available if you do not need wrapped-around results
+when overflow occurs (@pxref{Checking Integer Overflow}), or if you
+need to check for overflow in operations other than addition,
+subtraction, and multiplication (@pxref{Integer Type Overflow}).
+
 @node Integer Type Overflow
 @subsection Integer Type Overflow
 
diff --git a/lib/intprops.h b/lib/intprops.h
index cb086715b..8cc8138f9 100644
--- a/lib/intprops.h
+++ b/lib/intprops.h
@@ -591,4 +591,33 @@
          : (tmin) / (a) < (b)) \
       : (tmax) / (b) < (a)))
 
+/* The following macros compute A + B, A - B, and A * B, respectively.
+   If no overflow occurs, they set *R to the result and return 1;
+   otherwise, they return 0 and may modify *R.
+
+   Example usage:
+
+     long int result;
+     if (INT_ADD_OK (a, b, &result))
+       printf ("result is %ld\n", result);
+     else
+       printf ("overflow\n");
+
+   A, B, and *R should be integers; they need not be the same type,
+   and they need not be all signed or all unsigned.
+
+   These macros work correctly on all known practical hosts, and do not rely
+   on undefined behavior due to signed arithmetic overflow.
+
+   These macros are not constant expressions.
+
+   These macros may evaluate their arguments zero or multiple times, so the
+   arguments should not have side effects.
+
+   These macros are tuned for B being a constant.  */
+
+#define INT_ADD_OK(a, b, r) ! INT_ADD_WRAPV (a, b, r)
+#define INT_SUBTRACT_OK(a, b, r) ! INT_SUBTRACT_WRAPV (a, b, r)
+#define INT_MULTIPLY_OK(a, b, r) ! INT_MULTIPLY_WRAPV (a, b, r)
+
 #endif /* _GL_INTPROPS_H */
-- 
2.27.0

Reply via email to