--- /dev/null
+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'])])