Overhaul `math' representation machinery.
[u/mdw/catacomb] / m4 / mdw-uint-bits.m4
diff --git a/m4/mdw-uint-bits.m4 b/m4/mdw-uint-bits.m4
new file mode 100644 (file)
index 0000000..d5e678f
--- /dev/null
@@ -0,0 +1,91 @@
+dnl -*-autoconf-*-
+
+### SYNOPSIS
+###
+###   dnl mdw_UINT_BITS(TYPE, ABBREV)
+###
+### DESCRIPTION
+###
+###   Defines ABBREV_BITS to be the number of bits representable by the
+###   unsigned type named TYPE.  This works even if the compiler in question
+###   is a cross-compiler, and correctly handles systems without 8-bit bytes,
+###   or which have hidden bits in their integer representations.
+###
+### LICENSE
+###
+###   Copyright (c) 2013 Mark Wooding <mdw@distorted.org.uk>
+###
+###   This program is free software: you can redistribute it and/or modify it
+###   under the terms of the GNU General Public License as published by the
+###   Free Software Foundation, either version 2 of the License, or (at your
+###   option) any later version.
+###
+###   This program is distributed in the hope that it will be useful, but
+###   WITHOUT ANY WARRANTY; without even the implied warranty of
+###   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+###   General Public License for more details.
+###
+###   You should have received a copy of the GNU General Public License along
+###   with this program. If not, see <http://www.gnu.org/licenses/>.
+###
+###   In particular, no exception to the GPL is granted regarding generated
+###   `configure' scripts which are the output of Autoconf.
+
+dnl Algorithm:
+dnl
+dnl We first find an upper bound on the number of bits in the type as
+dnl sizeof(TYPE) * CHAR_BIT.  This is usually a good guess at the actual
+dnl value, since most modern systems don't have hidden bits, so we test to
+dnl see if it's correct.  If not, we start a binary search to find the true
+dnl value.
+dnl
+dnl To test a guess n, we form X = (TYPE)~(TYPE)0, which is the largest value
+dnl representable as a TYPE, and attempt to shift it right by n places.  This
+dnl is a little subtle, since shifting by more than the word length is
+dnl undefined behaviour.  The implementation isn't completely perfect.  It
+dnl assumes that a shift of 15 bits must be allowed (which is reasonable,
+dnl since an int must be at least 16 bits, and the standard integer
+dnl promotions will map shorter integer types to an int).  If n > 15, we
+dnl shift down in two stages, once by dividing by 2^15 = 32768, and once by
+dnl shifting by n - 15; otherwise we just shift.  (This strange chicanery is
+dnl to deal with Microsoft compilers which incorrectly `optimize' adjacent
+dnl correct shifts into a single invalid one.)  If X >> n is nonzero then n
+dnl is too small; if X >> (n - 1) is zero then n is too large; if neither
+dnl condition holds then n is correct.
+dnl
+dnl This is, unfortunately, logarithmic if the initial guess is wrong, but
+dnl that will happen rarely on interesting platforms.  Sites wanting to speed
+dnl up the configuration process can pre-seed the configuration cache.  If
+dnl anyone really cares, we can detect a native compiler (as opposed to a
+dnl cross-compiler) and do the binary-search in C.
+
+# Serial 1
+AC_DEFUN([mdw_UINT_BITS],
+  [AC_CACHE_CHECK([size of type `$1' in bits], [mdw_cv_$2_bits],
+    [mdw_PROBE_CONSTANT([guess], [sizeof($1) * CHAR_BIT], [
+#include <limits.h>
+#ifdef HAVE_STDINT_H
+#  include <stdint.h>
+#endif])
+     down=0
+     while :; do
+       mdw_PROBE_CONSTANT([answer],
+                         [low($guess) ? -1 : low($guess - 1) ? 0 : +1], [
+#ifdef HAVE_STDINT_H
+#  include <stdint.h>
+#endif
+#define srs(x, n) ((n) <= 15 ? (x) >> (n) : ((x)/32768) >> ((n) - 15))
+#define max (($1)~($1)0 | 0u)
+#define low(b) (srs(max, b) != 0)
+])
+       case "$answer" in
+        0) break;;
+        1) up=$guess;;
+        -1) down=$guess;;
+       esac
+       guess=$(expr \( $up + $down \) / 2)
+     done
+     mdw_cv_$2_bits=$guess])
+   AS_TR_SH($2_bits)=$mdw_cv_$2_bits
+   AC_DEFINE_UNQUOTED(AS_TR_CPP([$2_BITS]), [$mdw_cv_$2_bits],
+                     [number of bits in an object of type `$1'])])