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