--- /dev/null
+dnl -*-autoconf-*-
+
+### SYNOPSIS
+###
+### mdw_PROBE_FLTFMT
+###
+### DESCRIPTION
+###
+### Attempts to determine the target system's floating-point formats.
+### The macros `FLT_FORMAT', `DBL_FORMAT', and `LDBL_FORMAT' are defined if
+### the corresponding formats are recognized. The values of these macros
+### are a bitmask:
+###
+### * `FLTFMT_ORGMASK' is a bitmask covering the `organization' field,
+### which identifies the organization which defined the format.
+###
+### -- `FLTFMT_IEEE' identifies IEEE 754.
+### -- `FLTFMT_INTEL' identifies Intel.
+###
+### * `FLTFMT_TYPEMASK' is a bitmask covering the `type' field, which
+### must be interpreted together with the organization field to
+### determine the format.
+###
+### -- `FLTFMT_IEEE_F32' is the IEEE 754 `binary32' format.
+### -- `FLTFMT_IEEE_F64' is the IEEE 754 `binary64' format.
+### -- `FLTFMT_IEEE_F128' is the IEEE 754 `binary128' format.
+### -- `FLTFMT_INTEL_F80' is the Intel x87 80-bit double-extended
+### format.
+###
+### * `FLTFMT_ENDMASK' is a bitmask covering the `endian' field, which
+### describes the byte ordering convention used.
+###
+### -- `FLTFMT_LE' means little-endian format.
+###
+### -- `FLTFMT_BE' means big-endian format.
+###
+### -- `FLTFMT_ARME' means `Acorn's ridiculous mixed-endian' format,
+### used by the ARM FPA, which stored the a `binary64' value as
+### two 32-bit words, most significant word first, but with the
+### individual words stored in little-endian byte order.
+###
+### Other conventions exist and support for them may be added later.
+###
+### Finally, there are predefined values for relevant combinations of
+### format codes and byte orderings:
+###
+### * `FLTFMT_IEEE_F32_LE' and `FLTFMT_IEEE_F32_BE';
+### * `FLTFMT_IEEE_F64_LE' and `FLTFMT_IEEE_F64_BE';
+### * `FLTFMT_IEEE_F128_LE' and `FLTFMT_IEEE_F128_BE';
+### * `FLTFMT_INTEL_F80_LE' and `FLTFMT_INTEL_F80_BE'.
+###
+### (I don't know of anything which uses Intel's double-extended format in
+### big-endian order, but it was easy enough to check for. The IEEE
+### formats are used on ARM64 and z/Architecture with opposite byte order.)
+###
+### This macro works correctly when cross-compiling.
+###
+### LICENSE
+###
+### Copyright (c) 2024 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 Principle of operation:
+dnl
+dnl The essential trick here lies in finding floating-point numbers whose
+dnl encoding, in various formats of interest, happen to be recognizable
+dnl diagnostic text strings. The structure definitions provide some space
+dnl for framing text which allows us to scrape the resulting diagnostic
+dnl strings from the object file.
+dnl
+dnl IEEE formats conveniently don't impose any restrictions on the contents
+dnl of the fraction field because there's a hidden bit. The Intel x87
+dnl double-extended format makes the most significant bit explicit, and also
+dnl expects normalization, which means that the top bit of the fraction bytes
+dnl must be set, so everything gets quite ugly. Worse, the actual data is 10
+dnl bytes long, but it sits in a 16-byte field to force alignment in vectors,
+dnl with the result that there are unavoidably zero bytes in our diagnostic
+dnl output. Different tools respond to these differently; e.g., GNU sed(1)
+dnl just writes out the zero bytes like they were any other character, while
+dnl Busybox sed(1) terminates the output line. As a result, we have to be
+dnl rather more flexible about matching this than I'd really like. (Messing
+dnl about with compiler-specific hacks for structure packing won't help here
+dnl because the analysis code still has to cope with compilers which don't
+dnl have those hacks.)
+
+# Serial 1
+AC_COPYRIGHT([
+Portions copyright (c) 2024 Mark Wooding.
+
+This configure script is free software: you can redistribute it and/or
+modify it under he 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.])
+
+AC_DEFUN([mdw_PROBE_FLTFMT],
+ [AC_CACHE_CHECK([floating-point representations], [mdw_cv_fltfmt],
+ [mdw_fltfmt=nil mdw_dblfmt=nil mdw_ldblfmt=nil
+ AC_LINK_IFELSE([AC_LANG_SOURCE([
+
+/* The following program is copyright (c) 2024 Mark Wooding. It 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/>.
+ */
+
+@%:@include <float.h>
+
+@%:@define DEFFLTDIAG(ty, type) \
+ struct fltdiag_@%:@@%:@ty { char top@<:@48@:>@; type x; char tail@<:@4@:>@; }
+
+DEFFLTDIAG(flt, float);
+
+static const struct fltdiag_flt flt_ieee_f32 = {
+ "\0\0\0\0\0\0\0\0\n@@@ mdw-probe-fltfmt float ieee-f32 = >",
+ 781.0352,
+ "<\n\0\0"
+};
+@%:@define DO_FLT_IEEE_F32 DO(flt_ieee_f32)
+
+DEFFLTDIAG(dbl, double);
+
+@%:@if DBL_MAX_10_EXP > 40
+static const struct fltdiag_dbl dbl_ieee_f64 = {
+ "\0\0\0\0\0\0\0\n@@@ mdw-probe-fltfmt double ieee-f64 = >",
+ 1.5839800103804824e40,
+ "<\n\0\0"
+};
+@%:@ define DO_DBL_IEEE_F64 DO(dbl_ieee_f64)
+@%:@else
+@%:@ define DO_DBL_IEEE_F64
+@%:@endif
+
+DEFFLTDIAG(ldbl, long double);
+
+@%:@if LDBL_MAX_10_EXP > 40
+static const struct fltdiag_ldbl ldbl_ieee_f64 = {
+ "\0\0\n@@@ mdw-probe-fltfmt long-double ieee-f64 = >",
+ 1.5839800103804824e40,
+ "<\n\0\0"
+};
+@%:@ define DO_LDBL_IEEE_F64 DO(ldbl_ieee_f64)
+@%:@else
+@%:@ define DO_LDBL_IEEE_F64
+@%:@endif
+
+@%:@if LDBL_MAX_10_EXP > 1257
+static const struct fltdiag_ldbl ldbl_ieee_f128 = {
+ "\0\n@@@ mdw-probe-fltfmt long-double ieee-f128 = >",
+ 1.6487728650847311136108983312706536e+1257L,
+ "<\n\0\0"
+};
+@%:@ define DO_LDBL_IEEE_F128 DO(ldbl_ieee_f128)
+@%:@else
+@%:@ define DO_LDBL_IEEE_F128
+@%:@endif
+
+@%:@if LDBL_MAX_10_EXP > 793
+static const struct fltdiag_ldbl ldbl_intel_f80 = {
+ "\0\n@@@ mdw-probe-fltfmt long-double intel-f80 = >",
+ 1.2806567921142816197e+793L,
+ "<\n\0\0"
+};
+@%:@ define DO_LDBL_INTEL_F80 DO(ldbl_intel_f80)
+@%:@else
+@%:@ define DO_LDBL_INTEL_F80
+@%:@endif
+
+@%:@include <stdio.h>
+int main(void)
+{
+@%:@define DO(var) fwrite(&var, sizeof(var), 1, stdout)
+ DO_FLT_IEEE_F32;
+ DO_DBL_IEEE_F64;
+ DO_LDBL_IEEE_F64;
+ DO_LDBL_IEEE_F128;
+ DO_LDBL_IEEE_F128;
+ DO_LDBL_INTEL_F80;
+@%:@undef DO
+ return (0);
+}
+])],
+ [sed -n "/^@@@ mdw-probe-fltfmt @<:@^ @:>@* @<:@^ @:>@* = >/p" \
+ conftest$EXEEXT >conftest.out
+ while read _at _tag ty fmt _eq diag; do
+ case $ty,$fmt,$diag in
+ "float,ieee-f32,>ABCD<") mdw_fltfmt=ieee-f32-le ;;
+ "float,ieee-f32,>DCBA<") mdw_fltfmt=ieee-f32-be ;;
+ "double,ieee-f64,>ABCDEFGH<") mdw_dblfmt=ieee-f64-le ;;
+ "double,ieee-f64,>EFGHABCD<") mdw_dblfmt=ieee-f64-arme ;;
+ "double,ieee-f64,>HGFEDCBA<") mdw_dblfmt=ieee-f64-be ;;
+ "long-double,ieee-f64,>ABCDEFGH<") mdw_ldblfmt=ieee-f64-le ;;
+ "long-double,ieee-f64,>HGFEDCBA<") mdw_ldblfmt=ieee-f64-be ;;
+ "long-double,ieee-f128,>ABCDEFGHIJKLMNOP<")
+ mdw_ldblfmt=ieee-f128-le ;;
+ "long-double,ieee-f128,>PONMLKJIHGFEDCBA<")
+ mdw_ldblfmt=ieee-f128-be ;;
+ "long-double,intel-f80,>ABCDEFGÈIJ" | \
+ "long-double,intel-f80,>ABCDEFGÈIJ"*"<")
+ mdw_ldblfmt=intel-f80-le ;;
+ "long-double,intel-f80,>JIÈGFEDCBA" | \
+ "long-double,intel-f80,>JIÈGFEDCBA"*"<")
+ mdw_ldblfmt=intel-f80-be ;;
+ esac
+ done <conftest.out])
+ mdw_cv_fltfmt="float=$mdw_fltfmt"
+ mdw_cv_fltfmt="$mdw_cv_fltfmt double=$mdw_dblfmt"
+ mdw_cv_fltfmt="$mdw_cv_fltfmt long-double=$mdw_ldblfmt"])
+
+ AC_DEFINE([FLTFMT_ENDMASK], [0x0300],
+ [mask for byte ordering])
+ AC_DEFINE([FLTFMT_LE], [0x0000],
+ [little-endian floating-point storage])
+ AC_DEFINE([FLTFMT_BE], [0x0100],
+ [big-endian floating-point storage])
+ AC_DEFINE([FLTFMT_ARME], [0x0200],
+ [Acorn's ridiculous mixed-endian floating-point storage])
+
+ AC_DEFINE([FLTFMT_ORGMASK], [0xfc00],
+ [mask for floating-point organization id])
+ AC_DEFINE([FLTFMT_TYPEMASK], [0x00ff],
+ [mask for floating-point format type])
+
+ AC_DEFINE([FLTFMT_UNKNOWN], [0x0000],
+ [unrecognized floating-point format])
+
+ AC_DEFINE([FLTFMT_IEEE], [0x0400],
+ [floating-point organization id for IEEE 754])
+ AC_DEFINE([FLTFMT_IEEE_F32], [FLTFMT_IEEE | 5],
+ [IEEE 754 `binary32' format])
+ AC_DEFINE([FLTFMT_IEEE_F32_LE], [(FLTFMT_IEEE_F32 | FLTFMT_LE)],
+ [IEEE `binary32' format, little-endian])
+ AC_DEFINE([FLTFMT_IEEE_F32_BE], [(FLTFMT_IEEE_F32 | FLTFMT_BE)],
+ [IEEE `binary32' format, big-endian])
+ AC_DEFINE([FLTFMT_IEEE_F64], [FLTFMT_IEEE | 6],
+ [IEEE 754 `binary32' format])
+ AC_DEFINE([FLTFMT_IEEE_F64_LE], [(FLTFMT_IEEE_F64 | FLTFMT_LE)],
+ [IEEE `binary64' format, little-endian])
+ AC_DEFINE([FLTFMT_IEEE_F64_ARME], [(FLTFMT_IEEE_F64 | FLTFMT_ARME)],
+ [IEEE `binary64' format, Acorn ridiculous mixed-endian])
+ AC_DEFINE([FLTFMT_IEEE_F64_BE], [(FLTFMT_IEEE_F64 | FLTFMT_BE)],
+ [IEEE `binary64' format, big-endian])
+ AC_DEFINE([FLTFMT_IEEE_F128], [FLTFMT_IEEE | 7],
+ [IEEE 754 `binary32' format])
+ AC_DEFINE([FLTFMT_IEEE_F128_LE], [(FLTFMT_IEEE_F128 | FLTFMT_LE)],
+ [IEEE `binary128' format, little-endian])
+ AC_DEFINE([FLTFMT_IEEE_F128_BE], [(FLTFMT_IEEE_F128 | FLTFMT_BE)],
+ [IEEE `binary128' format, big-endian])
+
+ AC_DEFINE([FLTFMT_INTEL], [0x0800],
+ [floating-point organization id for Intel])
+ AC_DEFINE([FLTFMT_INTEL_F80], [FLTFMT_INTEL | 80],
+ [Intel x87 double-extended format])
+ AC_DEFINE([FLTFMT_INTEL_F80_LE], [(FLTFMT_INTEL_F80 | FLTFMT_LE)],
+ [Intel x86 double-extended format, little-endian])
+ AC_DEFINE([FLTFMT_INTEL_F80_BE], [(FLTFMT_INTEL_F80 | FLTFMT_BE)],
+ [Intel x87 double-extended format, big-endian])
+
+ AH_TEMPLATE([FLT_FORMAT],
+ [floating point format for `float' type])
+ AH_TEMPLATE([DBL_FORMAT],
+ [floating point format for `double' type])
+ AH_TEMPLATE([LDBL_FORMAT],
+ [floating point format for `long double' type])
+
+ for w in $mdw_cv_fltfmt; do
+ ty=${w%=*} fmt=${w@%:@*=}
+ case $ty in
+ float) var=FLT ;;
+ double) var=DBL ;;
+ long-double) var=LDBL ;;
+ *) AC_MSG_ERROR([unexpected floating-point type \`$ty']) ;;
+ esac
+ case $fmt in
+ nil) value=UNKNOWN ;;
+ ieee-f32-le) value=IEEE_F32_LE ;;
+ ieee-f32-be) value=IEEE_F32_BE ;;
+ ieee-f64-le) value=IEEE_F64_LE ;;
+ ieee-f64-arme) value=IEEE_F64_ARME ;;
+ ieee-f64-be) value=IEEE_F64_BE ;;
+ ieee-f128-le) value=IEEE_F128_LE ;;
+ ieee-f128-be) value=IEEE_F128_BE ;;
+ intel-f80-le) value=INTEL_F80_LE ;;
+ intel-f80-be) value=INTEL_F80_BE ;;
+ *) AC_MSG_ERROR([unexpected floating-point format \`$fmt']) ;;
+ esac
+ AC_DEFINE_UNQUOTED([${var}_FORMAT], [FLTFMT_$value])
+ done])