summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark H Weaver <mhw@netris.org>2014-03-11 21:27:21 -0400
committerMark H Weaver <mhw@netris.org>2014-03-11 21:39:31 -0400
commite293c94c65d49171c54bb1893c355e36c66806b8 (patch)
treeb6a62b474ff04bacfb51c776b663b7139896385e
parent7f8ad91b994d4922efdd7f9f89400b1ebddeed8f (diff)
downloadguile-e293c94c65d49171c54bb1893c355e36c66806b8.tar.gz
SCM_SRS: Improve fallback implemention to avoid unspecified behavior.
* libguile/numbers.h (SCM_SRS): Rewrite preprocessor test to avoid left-shifting negative integers, and to test more comprehensively for the behavior we need. Rewrite fallback implementation to avoid unspecified behavior.
-rw-r--r--libguile/numbers.h19
1 files changed, 14 insertions, 5 deletions
diff --git a/libguile/numbers.h b/libguile/numbers.h
index b4202f26a..a3271ccd5 100644
--- a/libguile/numbers.h
+++ b/libguile/numbers.h
@@ -49,12 +49,21 @@ typedef scm_t_int32 scm_t_wchar;
#define SCM_MOST_POSITIVE_FIXNUM ((SCM_T_SIGNED_BITS_MAX-3)/4)
#define SCM_MOST_NEGATIVE_FIXNUM (-SCM_MOST_POSITIVE_FIXNUM-1)
-/* SCM_SRS is signed right shift */
-#if (-1 == (((-1) << 2) + 2) >> 2)
-# define SCM_SRS(x, y) ((x) >> (y))
+/* SCM_SRS (X, Y) is signed right shift, defined as floor (X / 2^Y),
+ where Y must be non-negative and less than the width in bits of X.
+ It's common for >> to do this, but the C standards do not specify
+ what happens when X is negative.
+
+ NOTE: X must not perform side effects. */
+#if (-1 >> 2 == -1) && (-4 >> 2 == -1) && (-5 >> 2 == -2) && (-8 >> 2 == -2)
+# define SCM_SRS(x, y) ((x) >> (y))
#else
-# define SCM_SRS(x, y) ((x) < 0 ? ~((~(x)) >> (y)) : ((x) >> (y)))
-#endif /* (-1 == (((-1) << 2) + 2) >> 2) */
+# define SCM_SRS(x, y) \
+ ((x) < 0 \
+ ? -1 - (scm_t_signed_bits) (~(scm_t_bits)(x) >> (y)) \
+ : ((x) >> (y)))
+#endif
+
#define SCM_I_INUMP(x) (2 & SCM_UNPACK (x))