alloca \
alloca-opt \
assert \
+ byteswap \
c-ctype \
c-strtod \
exit \
+Thu Oct 26 20:19:00 2006 Ben Pfaff <blp@gnu.org>
+
+ * command.def: Add DEBUG FLOAT FORMAT.
+
Fri Oct 20 10:59:06 WST 2006 John Darrington <john@darrington.wattle.id.au>
* command.def: Added additional unimpl commands.
DEF_CMD (S_ANY, F_TESTING, "DEBUG EVALUATE", cmd_debug_evaluate)
DEF_CMD (S_ANY, F_TESTING, "DEBUG MOMENTS", cmd_debug_moments)
DEF_CMD (S_ANY, F_TESTING, "DEBUG POOL", cmd_debug_pool)
+DEF_CMD (S_ANY, F_TESTING, "DEBUG FLOAT FORMAT", cmd_debug_float_format)
DEF_CMD (S_ANY, F_TESTING, "DEBUG XFORM FAIL", cmd_debug_xform_fail)
/* Unimplemented commands. */
+Thu Oct 26 20:18:03 2006 Ben Pfaff <blp@gnu.org>
+
+ * lexer.c (parse_string): Make lexing of binary, hex, and octal
+ strings work (fixes bug #17948). Allow null bytes in strings, now
+ that there's a use for them (see tests/formats/float-format.sh).
+
Sun Jul 16 21:03:34 2006 Ben Pfaff <blp@gnu.org>
* format-parser.h: New file. Moved prototypes of format-parser.c
static int
parse_string (enum string_type type)
{
+ if (type != CHARACTER_STRING)
+ prog++;
+
/* Accumulate the entire string, joining sections indicated by +
signs. */
for (;;)
ds_truncate (&tokstr, 255);
}
- {
- /* FIXME. */
- size_t i;
- int warned = 0;
-
- for (i = 0; i < ds_length (&tokstr); i++)
- if (ds_cstr (&tokstr)[i] == 0)
- {
- if (!warned)
- {
- msg (SE, _("Sorry, literal strings may not contain null "
- "characters. Replacing with spaces."));
- warned = 1;
- }
- ds_cstr (&tokstr)[i] = ' ';
- }
- }
-
return T_STRING;
}
\f
+Thu Oct 26 20:19:19 2006 Ben Pfaff <blp@gnu.org>
+
+ * automake.mk: Add float-format.c.
+
+ * float-format.c: New file.
+
Sat Oct 14 16:21:45 2006 Ben Pfaff <blp@gnu.org>
* casefile-test.c: (test_casereader_clone) Free cases that we
src_language_tests_libtests_a_SOURCES = \
src/language/tests/casefile-test.c \
src/language/tests/moments-test.c \
- src/language/tests/pool-test.c
+ src/language/tests/pool-test.c \
+ src/language/tests/float-format.c
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+ Written by Ben Pfaff <blp@gnu.org>.
+
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include <config.h>
+
+#include <libpspp/float-format.h>
+
+#include "gettext.h"
+#include <inttypes.h>
+
+#include <language/command.h>
+#include <language/lexer/lexer.h>
+#include <libpspp/assertion.h>
+#include <libpspp/message.h>
+#include <libpspp/str.h>
+
+#define _(msgid) gettext (msgid)
+
+/* Maximum supported size of a floating-point number, in bytes. */
+#define FP_MAX_SIZE 32
+
+/* A floating-point number tagged with its representation. */
+struct fp
+ {
+ enum float_format format; /* Format. */
+ uint8_t data[FP_MAX_SIZE]; /* Representation. */
+ };
+
+/* Associates a format name with its identifier. */
+struct assoc
+ {
+ char name[4];
+ enum float_format format;
+ };
+
+/* List of floating-point formats. */
+static const struct assoc fp_formats[] =
+ {
+ {"ISL", FLOAT_IEEE_SINGLE_LE},
+ {"ISB", FLOAT_IEEE_SINGLE_BE},
+ {"IDL", FLOAT_IEEE_DOUBLE_LE},
+ {"IDB", FLOAT_IEEE_DOUBLE_BE},
+ {"VF", FLOAT_VAX_F},
+ {"VD", FLOAT_VAX_D},
+ {"VG", FLOAT_VAX_G},
+ {"ZS", FLOAT_Z_SHORT},
+ {"ZL", FLOAT_Z_LONG},
+ {"X", FLOAT_HEX},
+ {"FP", FLOAT_FP},
+ };
+static const size_t format_cnt = sizeof fp_formats / sizeof *fp_formats;
+
+/* Parses a floating-point format name into *FORMAT,
+ and returns success. */
+static bool
+parse_float_format (enum float_format *format)
+{
+ size_t i;
+
+ for (i = 0; i < format_cnt; i++)
+ if (lex_match_id (fp_formats[i].name))
+ {
+ *format = fp_formats[i].format;
+ return true;
+ }
+ lex_error ("expecting floating-point format identifier");
+ return false;
+}
+
+/* Returns the name for the given FORMAT. */
+static const char *
+get_float_format_name (enum float_format format)
+{
+ size_t i;
+
+ for (i = 0; i < format_cnt; i++)
+ if (fp_formats[i].format == format)
+ return fp_formats[i].name;
+
+ NOT_REACHED ();
+}
+
+/* Parses a number in the form FORMAT(STRING), where FORMAT is
+ the name of the format and STRING gives the number's
+ representation. Also supports ordinary floating-point numbers
+ written in decimal notation. Returns success. */
+static bool
+parse_fp (struct fp *fp)
+{
+ if (lex_is_number ())
+ {
+ double number = lex_number ();
+ fp->format = FLOAT_NATIVE_DOUBLE;
+ memcpy (fp->data, &number, sizeof number);
+ lex_get ();
+ }
+ else if (token == T_ID)
+ {
+ size_t length;
+
+ if (!parse_float_format (&fp->format)
+ || !lex_force_match ('(')
+ || !lex_force_string ())
+ return false;
+
+ length = ds_length (&tokstr);
+ if (fp->format != FLOAT_HEX)
+ {
+ if (length != float_get_size (fp->format))
+ {
+ msg (SE, _("%d-byte string needed but %d-byte string supplied."),
+ (int) float_get_size (fp->format), (int) length);
+ return false;
+ }
+ assert (length <= sizeof fp->data);
+ memcpy (fp->data, ds_data (&tokstr), length);
+ }
+ else
+ {
+ if (length >= sizeof fp->data)
+ {
+ msg (SE, _("Hexadecimal floating constant too long."));
+ return false;
+ }
+ strncpy ((char *) fp->data, ds_cstr (&tokstr), sizeof fp->data);
+ }
+
+ lex_get ();
+ if (!lex_force_match (')'))
+ return false;
+ }
+ else
+ {
+ lex_error (NULL);
+ return false;
+ }
+ return true;
+}
+
+/* Renders SRC, which contains SRC_SIZE bytes of a floating-point
+ number in the given FORMAT, as relatively human-readable
+ null-terminated string in the DST_SIZE bytes in DST. DST_SIZE
+ must be be large enough to hold the output. */
+static void
+make_printable (enum float_format format, const void *src_, size_t src_size,
+ char *dst, size_t dst_size)
+{
+ assert (dst_size >= 2 * src_size + 1);
+ if (format != FLOAT_HEX)
+ {
+ const uint8_t *src = src_;
+ while (src_size-- > 0)
+ {
+ sprintf (dst, "%02x", *src++);
+ dst += 2;
+ }
+ *dst = '\0';
+ }
+ else
+ strncpy (dst, src_, src_size + 1);
+}
+
+/* Checks that RESULT is identical to TO.
+ If so, returns false.
+ If not, issues a helpful error message that includes the given
+ CONVERSION_TYPE and the value that was converted FROM, and
+ returns true. */
+static bool
+mismatch (const struct fp *from, const struct fp *to, char *result,
+ const char *conversion_type)
+{
+ size_t to_size = float_get_size (to->format);
+ if (!memcmp (to->data, result, to_size))
+ return false;
+ else
+ {
+ size_t from_size = float_get_size (from->format);
+ char original[FP_MAX_SIZE * 2 + 1];
+ char expected[FP_MAX_SIZE * 2 + 1];
+ char actual[FP_MAX_SIZE * 2 + 1];
+ make_printable (from->format, from->data, from_size, original,
+ sizeof original);
+ make_printable (to->format, to->data, to_size, expected,
+ sizeof expected);
+ make_printable (to->format, result, to_size, actual, sizeof actual);
+ msg (SE,
+ _("%s conversion of %s from %s to %s should have produced %s "
+ "but actually produced %s."),
+ conversion_type,
+ original, get_float_format_name (from->format),
+ get_float_format_name (to->format), expected,
+ actual);
+ return true;
+ }
+}
+
+/* Checks that converting FROM into the format of TO yields
+ exactly the data in TO. */
+static bool
+verify_conversion (const struct fp *from, const struct fp *to)
+{
+ char tmp1[FP_MAX_SIZE], tmp2[FP_MAX_SIZE];
+
+ /* First try converting directly. */
+ float_convert (from->format, from->data, to->format, tmp1);
+ if (mismatch (from, to, tmp1, "Direct"))
+ return false;
+
+ /* Then convert via FLOAT_FP to prevent short-circuiting that
+ float_convert() does for some conversions (e.g. little<->big
+ endian for IEEE formats). */
+ float_convert (from->format, from->data, FLOAT_FP, tmp1);
+ float_convert (FLOAT_FP, tmp1, to->format, tmp2);
+ if (mismatch (from, to, tmp2, "Indirect"))
+ return false;
+
+ return true;
+}
+
+/* Executes the DEBUG FLOAT FORMAT command. */
+int
+cmd_debug_float_format (struct dataset *ds UNUSED)
+{
+ struct fp fp[16];
+ size_t fp_cnt = 0;
+ bool bijective = false;
+ bool ok;
+
+ for (;;)
+ {
+ if (fp_cnt >= sizeof fp / sizeof *fp)
+ {
+ msg (SE, _("Too many values in single command."));
+ return CMD_FAILURE;
+ }
+ if (!parse_fp (&fp[fp_cnt++]))
+ return CMD_FAILURE;
+
+ if (token == '.' && fp_cnt > 1)
+ break;
+ else if (!lex_force_match ('='))
+ return CMD_FAILURE;
+
+ if (fp_cnt == 1)
+ {
+ if (lex_match ('='))
+ bijective = true;
+ else if (lex_match (T_GT))
+ bijective = false;
+ else
+ {
+ lex_error (NULL);
+ return CMD_FAILURE;
+ }
+ }
+ else
+ {
+ if ((bijective && !lex_force_match ('='))
+ || (!bijective && !lex_force_match (T_GT)))
+ return CMD_FAILURE;
+ }
+ }
+
+ ok = true;
+ if (bijective)
+ {
+ size_t i, j;
+
+ for (i = 0; i < fp_cnt; i++)
+ for (j = 0; j < fp_cnt; j++)
+ if (!verify_conversion (&fp[i], &fp[j]))
+ ok = false;
+ }
+ else
+ {
+ size_t i;
+
+ for (i = 1; i < fp_cnt; i++)
+ if (!verify_conversion (&fp[i - 1], &fp[i]))
+ ok = false;
+ }
+
+ return ok ? CMD_SUCCESS : CMD_FAILURE;
+}
+Thu Oct 26 20:19:50 2006 Ben Pfaff <blp@gnu.org>
+
+ * automake.mk: Add the new files.
+
+ * legacy-encoding.c: New file.
+
+ * legacy-encoding.h: New file.
+
+ * float-format.c: New file.
+
+ * float-format.h: New file.
+
+ * integer-format.c: New file.
+
+ * integer-format.h: New file.
+
Sun Oct 15 09:49:50 WST 2006 John Darrington <john@darrington.wattle.id.au>
* hash.c hash.h: Added hsh_create_pool, a hash which uses a pool
src/libpspp/alloc.c \
src/libpspp/alloc.h \
src/libpspp/bit-vector.h \
+ src/libpspp/legacy-encoding.c \
+ src/libpspp/legacy-encoding.h \
src/libpspp/copyleft.c \
src/libpspp/copyleft.h \
src/libpspp/compiler.h \
+ src/libpspp/float-format.c \
+ src/libpspp/float-format.h \
src/libpspp/freaderror.c \
src/libpspp/freaderror.h \
src/libpspp/hash.c \
src/libpspp/hash.h \
src/libpspp/i18n.c \
src/libpspp/i18n.h \
+ src/libpspp/integer-format.c \
+ src/libpspp/integer-format.h \
src/libpspp/ll.c \
src/libpspp/ll.h \
src/libpspp/llx.c \
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+ Written by Ben Pfaff <blp@gnu.org>.
+
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include <config.h>
+
+#include <libpspp/float-format.h>
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/integer-format.h>
+#include <libpspp/str.h>
+
+#include "error.h"
+#include <byteswap.h>
+\f
+/* Neutral intermediate representation for binary floating-point numbers. */
+struct fp
+ {
+ enum
+ {
+ FINITE, /* Finite number (normalized or denormalized). */
+ INFINITE, /* Positive or negative infinity. */
+ NAN, /* Not a number. */
+
+ ZERO, /* Positive or negative zero. */
+ MISSING, /* System missing. */
+ LOWEST, /* LOWEST on e.g. missing values. */
+ HIGHEST, /* HIGHEST on e.g. missing values. */
+ RESERVED /* Special Vax representation. */
+ }
+ class;
+
+ enum
+ {
+ POSITIVE,
+ NEGATIVE
+ }
+ sign;
+
+ /* class == FINITE: The number has value "fraction *
+ 2**exponent", considering bit 63 in fraction to be just
+ right of the decimal point.
+
+ class == NAN: The fraction holds the significand, with its
+ leftmost bit in bit 63, so that signaling and quiet NaN
+ values can be preserved.
+
+ Unused for other classes. */
+ uint64_t fraction;
+ int exponent;
+ };
+
+static void extract_number (enum float_format, const void *, struct fp *);
+static void assemble_number (enum float_format, struct fp *, void *);
+
+static inline uint16_t get_uint16 (const void *);
+static inline uint32_t get_uint32 (const void *);
+static inline uint64_t get_uint64 (const void *);
+
+static inline void put_uint16 (uint16_t, void *);
+static inline void put_uint32 (uint32_t, void *);
+static inline void put_uint64 (uint64_t, void *);
+
+/* Converts SRC from format FROM to format TO, storing the
+ converted value into DST.
+ SRC and DST are permitted to arbitrarily overlap. */
+void
+float_convert (enum float_format from, const void *src,
+ enum float_format to, void *dst)
+{
+ if (from != to)
+ {
+ if ((from == FLOAT_IEEE_SINGLE_LE || from == FLOAT_IEEE_SINGLE_BE)
+ && (to == FLOAT_IEEE_SINGLE_LE || to == FLOAT_IEEE_SINGLE_BE))
+ put_uint32 (bswap_32 (get_uint32 (src)), dst);
+ else if ((from == FLOAT_IEEE_DOUBLE_LE || from == FLOAT_IEEE_DOUBLE_BE)
+ && (to == FLOAT_IEEE_DOUBLE_LE || to == FLOAT_IEEE_DOUBLE_BE))
+ put_uint64 (bswap_64 (get_uint64 (src)), dst);
+ else
+ {
+ struct fp fp;
+ extract_number (from, src, &fp);
+ assemble_number (to, &fp, dst);
+ }
+ }
+ else
+ {
+ if (src != dst)
+ memmove (dst, src, float_get_size (from));
+ }
+}
+
+/* Returns the number of bytes in a number in the given
+ FORMAT. */
+size_t
+float_get_size (enum float_format format)
+{
+ switch (format)
+ {
+ case FLOAT_IEEE_SINGLE_LE:
+ case FLOAT_IEEE_SINGLE_BE:
+ case FLOAT_VAX_F:
+ case FLOAT_Z_SHORT:
+ return 4;
+
+ case FLOAT_IEEE_DOUBLE_LE:
+ case FLOAT_IEEE_DOUBLE_BE:
+ case FLOAT_VAX_D:
+ case FLOAT_VAX_G:
+ case FLOAT_Z_LONG:
+ return 8;
+
+ case FLOAT_FP:
+ return sizeof (struct fp);
+
+ case FLOAT_HEX:
+ return 32;
+ }
+
+ NOT_REACHED ();
+}
+
+/* Attempts to identify the floating point format(s) in which the
+ LENGTH bytes in NUMBER represent the given EXPECTED_VALUE.
+ Returns the number of matches, which may be zero, one, or
+ greater than one. If a positive value is returned, then the
+ most likely candidate format (based on how common the formats
+ are in practice) is stored in *BEST_GUESS. */
+int
+float_identify (double expected_value, const void *number, size_t length,
+ enum float_format *best_guess)
+{
+ /* Candidates for identification in order of decreasing
+ preference. */
+ enum float_format candidates[] =
+ {
+ FLOAT_IEEE_SINGLE_LE,
+ FLOAT_IEEE_SINGLE_BE,
+ FLOAT_IEEE_DOUBLE_LE,
+ FLOAT_IEEE_DOUBLE_BE,
+ FLOAT_VAX_F,
+ FLOAT_VAX_D,
+ FLOAT_VAX_G,
+ FLOAT_Z_SHORT,
+ FLOAT_Z_LONG,
+ };
+ const size_t candidate_cnt = sizeof candidates / sizeof *candidates;
+
+ enum float_format *p;
+ int match_cnt;
+
+ match_cnt = 0;
+ for (p = candidates; p < candidates + candidate_cnt; p++)
+ if (float_get_size (*p) == length)
+ {
+ char tmp[8];
+ assert (sizeof tmp >= float_get_size (*p));
+ float_convert (FLOAT_NATIVE_DOUBLE, &expected_value, *p, tmp);
+ if (!memcmp (tmp, number, length) && match_cnt++ == 0)
+ *best_guess = *p;
+ }
+ return match_cnt;
+}
+\f
+/* Returns CNT bits in X starting from the given bit OFS. */
+static inline uint64_t
+get_bits (uint64_t x, int ofs, int cnt)
+{
+ assert (ofs >= 0 && ofs < 64);
+ assert (cnt > 0 && cnt < 64);
+ assert (ofs + cnt <= 64);
+ return (x >> ofs) & ((UINT64_C(1) << cnt) - 1);
+}
+
+/* Returns the 16-bit unsigned integer at P,
+ which need not be aligned. */
+static inline uint16_t
+get_uint16 (const void *p)
+{
+ uint16_t x;
+ memcpy (&x, p, sizeof x);
+ return x;
+}
+
+/* Returns the 32-bit unsigned integer at P,
+ which need not be aligned. */
+static inline uint32_t
+get_uint32 (const void *p)
+{
+ uint32_t x;
+ memcpy (&x, p, sizeof x);
+ return x;
+}
+
+/* Returns the 64-bit unsigned integer at P,
+ which need not be aligned. */
+static inline uint64_t
+get_uint64 (const void *p)
+{
+ uint64_t x;
+ memcpy (&x, p, sizeof x);
+ return x;
+}
+
+/* Stores 16-bit unsigned integer X at P,
+ which need not be aligned. */
+static inline void
+put_uint16 (uint16_t x, void *p)
+{
+ memcpy (p, &x, sizeof x);
+}
+
+/* Stores 32-bit unsigned integer X at P,
+ which need not be aligned. */
+static inline void
+put_uint32 (uint32_t x, void *p)
+{
+ memcpy (p, &x, sizeof x);
+}
+
+/* Stores 64-bit unsigned integer X at P,
+ which need not be aligned. */
+static inline void
+put_uint64 (uint64_t x, void *p)
+{
+ memcpy (p, &x, sizeof x);
+}
+
+/* Returns NATIVE converted to a form that, when stored in
+ memory, will be in little-endian byte order. */
+static inline uint16_t
+native_to_le16 (uint16_t native)
+{
+ return INTEGER_NATIVE == INTEGER_LSB_FIRST ? native : bswap_16 (native);
+}
+
+/* Returns NATIVE converted to a form that, when stored in
+ memory, will be in big-endian byte order. */
+static inline uint16_t
+native_to_be16 (uint16_t native)
+{
+ return INTEGER_NATIVE == INTEGER_MSB_FIRST ? native : bswap_16 (native);
+}
+
+/* Returns NATIVE converted to a form that, when stored in
+ memory, will be in VAX-endian byte order. */
+static inline uint16_t
+native_to_vax16 (uint16_t native)
+{
+ return native_to_le16 (native);
+}
+
+/* Returns NATIVE converted to a form that, when stored in
+ memory, will be in little-endian byte order. */
+static inline uint32_t
+native_to_le32 (uint32_t native)
+{
+ return INTEGER_NATIVE == INTEGER_LSB_FIRST ? native : bswap_32 (native);
+}
+
+/* Returns NATIVE converted to a form that, when stored in
+ memory, will be in big-endian byte order. */
+static inline uint32_t
+native_to_be32 (uint32_t native)
+{
+ return INTEGER_NATIVE == INTEGER_MSB_FIRST ? native : bswap_32 (native);
+}
+
+/* Returns NATIVE converted to a form that, when stored in
+ memory, will be in VAX-endian byte order. */
+static inline uint32_t
+native_to_vax32 (uint32_t native)
+{
+ return native_to_be32 (((native & 0xff00ff00) >> 8) |
+ ((native & 0x00ff00ff) << 8));
+}
+
+/* Returns NATIVE converted to a form that, when stored in
+ memory, will be in little-endian byte order. */
+static inline uint64_t
+native_to_le64 (uint64_t native)
+{
+ return INTEGER_NATIVE == INTEGER_LSB_FIRST ? native : bswap_64 (native);
+}
+
+/* Returns NATIVE converted to a form that, when stored in
+ memory, will be in big-endian byte order. */
+static inline uint64_t
+native_to_be64 (uint64_t native)
+{
+ return INTEGER_NATIVE == INTEGER_MSB_FIRST ? native : bswap_64 (native);
+}
+
+/* Returns NATIVE converted to a form that, when stored in
+ memory, will be in VAX-endian byte order. */
+static inline uint64_t
+native_to_vax64 (uint64_t native)
+{
+ return native_to_be64 (((native & UINT64_C(0xff00ff0000000000)) >> 40) |
+ ((native & UINT64_C(0x00ff00ff00000000)) >> 24) |
+ ((native & UINT64_C(0x00000000ff00ff00)) << 24) |
+ ((native & UINT64_C(0x0000000000ff00ff)) << 40));
+}
+
+/* Given LE, obtained from memory in little-endian format,
+ returns its value. */
+static inline uint16_t
+le_to_native16 (uint16_t le)
+{
+ return INTEGER_NATIVE == INTEGER_LSB_FIRST ? le : bswap_16 (le);
+}
+
+/* Given BE, obtained from memory in big-endian format, returns
+ its value. */
+static inline uint16_t
+be_to_native16 (uint16_t be)
+{
+ return INTEGER_NATIVE == INTEGER_MSB_FIRST ? be : bswap_16 (be);
+}
+
+/* Given VAX, obtained from memory in VAX-endian format, returns
+ its value. */
+static inline uint16_t
+vax_to_native16 (uint16_t vax)
+{
+ return le_to_native16 (vax);
+}
+
+/* Given LE, obtained from memory in little-endian format,
+ returns its value. */
+static inline uint32_t
+le_to_native32 (uint32_t le)
+{
+ return INTEGER_NATIVE == INTEGER_LSB_FIRST ? le : bswap_32 (le);
+}
+
+/* Given BE, obtained from memory in big-endian format, returns
+ its value. */
+static inline uint32_t
+be_to_native32 (uint32_t be)
+{
+ return INTEGER_NATIVE == INTEGER_MSB_FIRST ? be : bswap_32 (be);
+}
+
+/* Given VAX, obtained from memory in VAX-endian format, returns
+ its value. */
+static inline uint32_t
+vax_to_native32 (uint32_t vax)
+{
+ uint32_t be = be_to_native32 (vax);
+ return ((be & 0xff00ff00) >> 8) | ((be & 0x00ff00ff) << 8);
+}
+
+/* Given LE, obtained from memory in little-endian format,
+ returns its value. */
+static inline uint64_t
+le_to_native64 (uint64_t le)
+{
+ return INTEGER_NATIVE == INTEGER_LSB_FIRST ? le : bswap_64 (le);
+}
+
+/* Given BE, obtained from memory in big-endian format, returns
+ its value. */
+static inline uint64_t
+be_to_native64 (uint64_t be)
+{
+ return INTEGER_NATIVE == INTEGER_MSB_FIRST ? be : bswap_64 (be);
+}
+
+/* Given VAX, obtained from memory in VAX-endian format, returns
+ its value. */
+static inline uint64_t
+vax_to_native64 (uint64_t vax)
+{
+ uint64_t be = be_to_native64 (vax);
+ return (((be & UINT64_C(0xff00ff0000000000)) >> 40) |
+ ((be & UINT64_C(0x00ff00ff00000000)) >> 24) |
+ ((be & UINT64_C(0x00000000ff00ff00)) << 24) |
+ ((be & UINT64_C(0x0000000000ff00ff)) << 40));
+}
+\f
+static void extract_ieee (uint64_t, int exp_bits, int frac_bits, struct fp *);
+static void extract_vax (uint64_t, int exp_bits, int frac_bits, struct fp *);
+static void extract_z (uint64_t, int exp_bits, int frac_bits, struct fp *);
+static void extract_hex (const char *, struct fp *);
+
+/* Converts the number at BITS from format TYPE into neutral
+ format at FP. */
+static void
+extract_number (enum float_format type, const void *bits, struct fp *fp)
+{
+ switch (type)
+ {
+ case FLOAT_IEEE_SINGLE_LE:
+ extract_ieee (le_to_native32 (get_uint32 (bits)), 8, 23, fp);
+ break;
+ case FLOAT_IEEE_SINGLE_BE:
+ extract_ieee (be_to_native32 (get_uint32 (bits)), 8, 23, fp);
+ break;
+ case FLOAT_IEEE_DOUBLE_LE:
+ extract_ieee (le_to_native64 (get_uint64 (bits)), 11, 52, fp);
+ break;
+ case FLOAT_IEEE_DOUBLE_BE:
+ extract_ieee (be_to_native64 (get_uint64 (bits)), 11, 52, fp);
+ break;
+
+ case FLOAT_VAX_F:
+ extract_vax (vax_to_native32 (get_uint32 (bits)), 8, 23, fp);
+ break;
+ case FLOAT_VAX_D:
+ extract_vax (vax_to_native64 (get_uint64 (bits)), 8, 55, fp);
+ break;
+ case FLOAT_VAX_G:
+ extract_vax (vax_to_native64 (get_uint64 (bits)), 11, 52, fp);
+ break;
+
+ case FLOAT_Z_SHORT:
+ extract_z (be_to_native32 (get_uint32 (bits)), 7, 24, fp);
+ break;
+ case FLOAT_Z_LONG:
+ extract_z (be_to_native64 (get_uint64 (bits)), 7, 56, fp);
+ break;
+
+ case FLOAT_FP:
+ memcpy (fp, bits, sizeof *fp);
+ break;
+ case FLOAT_HEX:
+ extract_hex (bits, fp);
+ break;
+ }
+
+ assert (!(fp->class == FINITE && fp->fraction == 0));
+}
+
+/* Converts the IEEE format number in BITS, which has EXP_BITS of
+ exponent and FRAC_BITS of fraction, into neutral format at
+ FP. */
+static void
+extract_ieee (uint64_t bits, int exp_bits, int frac_bits, struct fp *fp)
+{
+ const int bias = (1 << (exp_bits - 1)) - 1;
+ const uint64_t max_raw_frac = (UINT64_C(1) << frac_bits) - 1;
+ const int max_raw_exp = (1 << exp_bits) - 1;
+
+ const uint64_t raw_frac = get_bits (bits, 0, frac_bits);
+ const int raw_exp = get_bits (bits, frac_bits, exp_bits);
+ const bool raw_sign = get_bits (bits, frac_bits + exp_bits, 1);
+
+ if (raw_sign && raw_exp == max_raw_exp - 1 && raw_frac == max_raw_frac - 1)
+ fp->class = LOWEST;
+ else if (raw_exp == max_raw_exp - 1 && raw_frac == max_raw_frac)
+ fp->class = raw_sign ? MISSING : HIGHEST;
+ else if (raw_exp == max_raw_exp)
+ {
+ if (raw_frac == 0)
+ fp->class = INFINITE;
+ else
+ {
+ fp->class = NAN;
+ fp->fraction = raw_frac << (64 - frac_bits);
+ }
+ }
+ else if (raw_exp == 0)
+ {
+ if (raw_frac != 0)
+ {
+ fp->class = FINITE;
+ fp->exponent = 1 - bias;
+ fp->fraction = raw_frac << (64 - frac_bits);
+ }
+ else
+ fp->class = ZERO;
+ }
+ else
+ {
+ fp->class = FINITE;
+ fp->exponent = raw_exp - bias + 1;
+ fp->fraction = (raw_frac << (64 - frac_bits - 1)) | (UINT64_C(1) << 63);
+ }
+
+ fp->sign = raw_sign ? NEGATIVE : POSITIVE;
+}
+
+/* Converts the VAX format number in BITS, which has EXP_BITS of
+ exponent and FRAC_BITS of fraction, into neutral format at
+ FP. */
+static void
+extract_vax (uint64_t bits, int exp_bits, int frac_bits, struct fp *fp)
+{
+ const int bias = 1 << (exp_bits - 1);
+ const uint64_t max_raw_frac = (UINT64_C(1) << frac_bits) - 1;
+ const int max_raw_exp = (1 << exp_bits) - 1;
+
+ const uint64_t raw_frac = get_bits (bits, 0, frac_bits);
+ const int raw_exp = get_bits (bits, frac_bits, exp_bits);
+ const bool raw_sign = get_bits (bits, frac_bits + exp_bits, 1);
+
+ if (raw_sign && raw_exp == max_raw_exp && raw_frac == max_raw_frac - 1)
+ fp->class = LOWEST;
+ else if (raw_exp == max_raw_exp && raw_frac == max_raw_frac)
+ fp->class = raw_sign ? MISSING : HIGHEST;
+ else if (raw_exp == 0)
+ fp->class = raw_sign == 0 ? ZERO : RESERVED;
+ else
+ {
+ fp->class = FINITE;
+ fp->fraction = (raw_frac << (64 - frac_bits - 1)) | (UINT64_C(1) << 63);
+ fp->exponent = raw_exp - bias;
+ }
+
+ fp->sign = raw_sign ? NEGATIVE : POSITIVE;
+}
+
+/* Converts the Z architecture format number in BITS, which has
+ EXP_BITS of exponent and FRAC_BITS of fraction, into neutral
+ format at FP. */
+static void
+extract_z (uint64_t bits, int exp_bits, int frac_bits, struct fp *fp)
+{
+ const int bias = 1 << (exp_bits - 1);
+ const uint64_t max_raw_frac = (UINT64_C(1) << frac_bits) - 1;
+ const int max_raw_exp = (1 << exp_bits) - 1;
+
+ uint64_t raw_frac = get_bits (bits, 0, frac_bits);
+ int raw_exp = get_bits (bits, frac_bits, exp_bits);
+ int raw_sign = get_bits (bits, frac_bits + exp_bits, 1);
+
+ fp->sign = raw_sign ? NEGATIVE : POSITIVE;
+ if (raw_exp == max_raw_exp && raw_frac == max_raw_frac)
+ fp->class = raw_sign ? MISSING : HIGHEST;
+ else if (raw_sign && raw_exp == max_raw_exp && raw_frac == max_raw_frac - 1)
+ fp->class = LOWEST;
+ else if (raw_frac != 0)
+ {
+ fp->class = FINITE;
+ fp->fraction = raw_frac << (64 - frac_bits);
+ fp->exponent = (raw_exp - bias) * 4;
+ }
+ else
+ fp->class = ZERO;
+}
+
+/* Returns the integer value of hex digit C. */
+static int
+hexit_value (int c)
+{
+ const char s[] = "0123456789abcdef";
+ const char *cp = strchr (s, tolower ((unsigned char) c));
+
+ assert (cp != NULL);
+ return cp - s;
+}
+
+/* Parses a hexadecimal floating point number string at S (useful
+ for testing) into neutral format at FP. */
+static void
+extract_hex (const char *s, struct fp *fp)
+{
+ if (*s == '-')
+ {
+ fp->sign = NEGATIVE;
+ s++;
+ }
+ else
+ fp->sign = POSITIVE;
+
+ if (!strcmp (s, "Infinity"))
+ fp->class = INFINITE;
+ else if (!strcmp (s, "Missing"))
+ fp->class = MISSING;
+ else if (!strcmp (s, "Lowest"))
+ fp->class = LOWEST;
+ else if (!strcmp (s, "Highest"))
+ fp->class = HIGHEST;
+ else if (!strcmp (s, "Reserved"))
+ fp->class = RESERVED;
+ else
+ {
+ int offset;
+
+ if (!memcmp (s, "NaN:", 4))
+ {
+ fp->class = NAN;
+ s += 4;
+ }
+ else
+ fp->class = FINITE;
+
+ if (*s == '.')
+ s++;
+
+ fp->exponent = 0;
+ fp->fraction = 0;
+ offset = 60;
+ for (; isxdigit ((unsigned char) *s); s++)
+ if (offset >= 0)
+ {
+ uint64_t digit = hexit_value (*s);
+ fp->fraction += digit << offset;
+ offset -= 4;
+ }
+
+ if (fp->class == FINITE)
+ {
+ if (fp->fraction == 0)
+ fp->class = ZERO;
+ else if (*s == 'p')
+ {
+ char *tail;
+ fp->exponent += strtol (s + 1, &tail, 10);
+ s = tail;
+ }
+ }
+ }
+}
+\f
+static uint64_t assemble_ieee (struct fp *, int exp_bits, int frac_bits);
+static uint64_t assemble_vax (struct fp *, int exp_bits, int frac_bits);
+static uint64_t assemble_z (struct fp *, int exp_bits, int frac_bits);
+static void assemble_hex (struct fp *, void *);
+
+/* Converts the neutral format floating point number in FP into
+ format TYPE at NUMBER. May modify FP as part of the
+ process. */
+static void
+assemble_number (enum float_format type, struct fp *fp, void *number)
+{
+ switch (type)
+ {
+ case FLOAT_IEEE_SINGLE_LE:
+ put_uint32 (native_to_le32 (assemble_ieee (fp, 8, 23)), number);
+ break;
+ case FLOAT_IEEE_SINGLE_BE:
+ put_uint32 (native_to_be32 (assemble_ieee (fp, 8, 23)), number);
+ break;
+ case FLOAT_IEEE_DOUBLE_LE:
+ put_uint64 (native_to_le64 (assemble_ieee (fp, 11, 52)), number);
+ break;
+ case FLOAT_IEEE_DOUBLE_BE:
+ put_uint64 (native_to_be64 (assemble_ieee (fp, 11, 52)), number);
+ break;
+
+ case FLOAT_VAX_F:
+ put_uint32 (native_to_vax32 (assemble_vax (fp, 8, 23)), number);
+ break;
+ case FLOAT_VAX_D:
+ put_uint64 (native_to_vax64 (assemble_vax (fp, 8, 55)), number);
+ break;
+ case FLOAT_VAX_G:
+ put_uint64 (native_to_vax64 (assemble_vax (fp, 11, 52)), number);
+ break;
+
+ case FLOAT_Z_SHORT:
+ put_uint64 (native_to_be32 (assemble_z (fp, 7, 24)), number);
+ break;
+ case FLOAT_Z_LONG:
+ put_uint64 (native_to_be64 (assemble_z (fp, 7, 56)), number);
+ break;
+
+ case FLOAT_FP:
+ memcpy (number, fp, sizeof *fp);
+ break;
+ case FLOAT_HEX:
+ assemble_hex (fp, number);
+ break;
+ }
+}
+
+/* Rounds off FP's fraction to FRAC_BITS bits of precision.
+ Halfway values are rounded to even. */
+static void
+normalize_and_round_fp (struct fp *fp, int frac_bits)
+{
+ assert (fp->class == FINITE);
+ assert (fp->fraction != 0);
+
+ /* Make sure that the leading fraction bit is 1. */
+ while (!(fp->fraction & (UINT64_C(1) << 63)))
+ {
+ fp->fraction <<= 1;
+ fp->exponent--;
+ }
+
+ if (frac_bits < 64)
+ {
+ uint64_t last_frac_bit = UINT64_C(1) << (64 - frac_bits);
+ uint64_t decision_bit = last_frac_bit >> 1;
+ if (fp->fraction & decision_bit
+ && (fp->fraction & (decision_bit - 1)
+ || fp->fraction & last_frac_bit))
+ {
+ fp->fraction += last_frac_bit;
+ if ((fp->fraction >> 63) == 0)
+ {
+ fp->fraction = UINT64_C(1) << 63;
+ fp->exponent++;
+ }
+ }
+
+ /* Mask off all but FRAC_BITS high-order bits.
+ If we rounded up, then these bits no longer have
+ meaningful values. */
+ fp->fraction &= ~(last_frac_bit - 1);
+ }
+}
+
+/* Converts the neutral format floating point number in FP into
+ IEEE format with EXP_BITS exponent bits and FRAC_BITS fraction
+ bits, and returns the value. */
+static uint64_t
+assemble_ieee (struct fp *fp, int exp_bits, int frac_bits)
+{
+ const uint64_t max_raw_frac = (UINT64_C(1) << frac_bits) - 1;
+
+ const int bias = (1 << (exp_bits - 1)) - 1;
+ const int max_raw_exp = (1 << exp_bits) - 1;
+ const int min_norm_exp = 1 - bias;
+ const int min_denorm_exp = min_norm_exp - frac_bits;
+ const int max_norm_exp = max_raw_exp - 1 - bias;
+
+ uint64_t raw_frac;
+ int raw_exp;
+ bool raw_sign;
+
+ raw_sign = fp->sign != POSITIVE;
+
+ switch (fp->class)
+ {
+ case FINITE:
+ normalize_and_round_fp (fp, frac_bits + 1);
+ if (fp->exponent - 1 > max_norm_exp)
+ {
+ /* Overflow to infinity. */
+ raw_exp = max_raw_exp;
+ raw_frac = 0;
+ }
+ else if (fp->exponent - 1 >= min_norm_exp)
+ {
+ /* Normal. */
+ raw_frac = (fp->fraction << 1) >> (64 - frac_bits);
+ raw_exp = (fp->exponent - 1) + bias;
+ }
+ else if (fp->exponent - 1 >= min_denorm_exp)
+ {
+ /* Denormal. */
+ const int denorm_shift = min_norm_exp - fp->exponent;
+ raw_frac = (fp->fraction >> (64 - frac_bits)) >> denorm_shift;
+ raw_exp = 0;
+ }
+ else
+ {
+ /* Underflow to zero. */
+ raw_frac = 0;
+ raw_exp = 0;
+ }
+ break;
+
+ case INFINITE:
+ raw_frac = 0;
+ raw_exp = max_raw_exp;
+ break;
+
+ case NAN:
+ raw_frac = fp->fraction >> (64 - frac_bits);
+ if (raw_frac == 0)
+ raw_frac = 1;
+ raw_exp = max_raw_exp;
+ break;
+
+ case ZERO:
+ raw_frac = 0;
+ raw_exp = 0;
+ break;
+
+ case MISSING:
+ raw_sign = 1;
+ raw_exp = max_raw_exp - 1;
+ raw_frac = max_raw_frac;
+ break;
+
+ case LOWEST:
+ raw_sign = 1;
+ raw_exp = max_raw_exp - 1;
+ raw_frac = max_raw_frac - 1;
+ break;
+
+ case HIGHEST:
+ raw_sign = 0;
+ raw_exp = max_raw_exp - 1;
+ raw_frac = max_raw_frac;
+ break;
+
+ case RESERVED:
+ /* Convert to what processors commonly treat as signaling NAN. */
+ raw_frac = (UINT64_C(1) << frac_bits) - 1;
+ raw_exp = max_raw_exp;
+ break;
+
+ default:
+ NOT_REACHED ();
+ }
+
+ return (((uint64_t) raw_sign << (frac_bits + exp_bits))
+ | ((uint64_t) raw_exp << frac_bits)
+ | raw_frac);
+}
+
+/* Converts the neutral format floating point number in FP into
+ VAX format with EXP_BITS exponent bits and FRAC_BITS fraction
+ bits, and returns the value. */
+static uint64_t
+assemble_vax (struct fp *fp, int exp_bits, int frac_bits)
+{
+ const int max_raw_exp = (1 << exp_bits) - 1;
+ const int bias = 1 << (exp_bits - 1);
+ const int min_finite_exp = 1 - bias;
+ const int max_finite_exp = max_raw_exp - bias;
+ const uint64_t max_raw_frac = (UINT64_C(1) << frac_bits) - 1;
+
+ uint64_t raw_frac;
+ int raw_exp;
+ bool raw_sign;
+
+ raw_sign = fp->sign != POSITIVE;
+
+ switch (fp->class)
+ {
+ case FINITE:
+ normalize_and_round_fp (fp, frac_bits + 1);
+ if (fp->exponent > max_finite_exp)
+ {
+ /* Overflow to reserved operand. */
+ raw_sign = 1;
+ raw_exp = 0;
+ raw_frac = 0;
+ }
+ else if (fp->exponent >= min_finite_exp)
+ {
+ /* Finite. */
+ raw_frac = (fp->fraction << 1) >> (64 - frac_bits);
+ raw_exp = fp->exponent + bias;
+ }
+ else
+ {
+ /* Underflow to zero. */
+ raw_sign = 0;
+ raw_frac = 0;
+ raw_exp = 0;
+ }
+ break;
+
+ case INFINITE:
+ case NAN:
+ case RESERVED:
+ raw_sign = 1;
+ raw_exp = 0;
+ raw_frac = 0;
+ break;
+
+ case ZERO:
+ raw_sign = 0;
+ raw_frac = 0;
+ raw_exp = 0;
+ break;
+
+ case MISSING:
+ raw_sign = 1;
+ raw_exp = max_finite_exp + bias;
+ raw_frac = max_raw_frac;
+ break;
+
+ case LOWEST:
+ raw_sign = 1;
+ raw_exp = max_finite_exp + bias;
+ raw_frac = max_raw_frac - 1;
+ break;
+
+ case HIGHEST:
+ raw_sign = 0;
+ raw_exp = max_finite_exp + bias;
+ raw_frac = max_raw_frac;
+ break;
+
+ default:
+ NOT_REACHED ();
+ }
+
+ return (((uint64_t) raw_sign << (frac_bits + exp_bits))
+ | ((uint64_t) raw_exp << frac_bits)
+ | raw_frac);
+}
+
+/* Shift right until the exponent is a multiple of 4.
+ Rounding is not needed, because none of the formats we support
+ has more than 53 bits of precision. That is, we never shift a
+ 1-bit off the right end of the fraction. */
+static void
+normalize_hex_fp (struct fp *fp)
+{
+ while (fp->exponent % 4)
+ {
+ fp->fraction >>= 1;
+ fp->exponent++;
+ }
+}
+
+/* Converts the neutral format floating point number in FP into Z
+ architecture format with EXP_BITS exponent bits and FRAC_BITS
+ fraction bits, and returns the value. */
+static uint64_t
+assemble_z (struct fp *fp, int exp_bits, int frac_bits)
+{
+ const int max_raw_exp = (1 << exp_bits) - 1;
+ const int bias = 1 << (exp_bits - 1);
+ const int max_norm_exp = (max_raw_exp - bias) * 4;
+ const int min_norm_exp = -bias * 4;
+ const int min_denorm_exp = min_norm_exp - (frac_bits - 1);
+
+ const uint64_t max_raw_frac = (UINT64_C(1) << frac_bits) - 1;
+
+ uint64_t raw_frac;
+ int raw_exp;
+ int raw_sign;
+
+ raw_sign = fp->sign != POSITIVE;
+
+ switch (fp->class)
+ {
+ case FINITE:
+ normalize_and_round_fp (fp, frac_bits);
+ normalize_hex_fp (fp);
+ if (fp->exponent > max_norm_exp)
+ {
+ /* Overflow to largest value. */
+ raw_exp = max_raw_exp;
+ raw_frac = max_raw_frac;
+ }
+ else if (fp->exponent >= min_norm_exp)
+ {
+ /* Normal. */
+ raw_frac = fp->fraction >> (64 - frac_bits);
+ raw_exp = (fp->exponent / 4) + bias;
+ }
+ else if (fp->exponent >= min_denorm_exp)
+ {
+ /* Denormal. */
+ const int denorm_shift = min_norm_exp - fp->exponent;
+ raw_frac = (fp->fraction >> (64 - frac_bits)) >> denorm_shift;
+ raw_exp = 0;
+ }
+ else
+ {
+ /* Underflow to zero. */
+ raw_frac = 0;
+ raw_exp = 0;
+ }
+ break;
+
+ case INFINITE:
+ /* Overflow to largest value. */
+ raw_exp = max_raw_exp;
+ raw_frac = max_raw_frac;
+ break;
+
+ case NAN:
+ case RESERVED:
+ case ZERO:
+ /* Treat all of these as zero, because Z architecture
+ doesn't have any reserved values. */
+ raw_exp = 0;
+ raw_frac = 0;
+ break;
+
+ case MISSING:
+ raw_sign = 1;
+ raw_exp = max_raw_exp;
+ raw_frac = max_raw_frac;
+ break;
+
+ case LOWEST:
+ raw_sign = 1;
+ raw_exp = max_raw_exp;
+ raw_frac = max_raw_frac - 1;
+ break;
+
+ case HIGHEST:
+ raw_sign = 0;
+ raw_exp = max_raw_exp;
+ raw_frac = max_raw_frac;
+ break;
+
+ default:
+ NOT_REACHED ();
+ }
+
+ return (((uint64_t) raw_sign << (frac_bits + exp_bits))
+ | ((uint64_t) raw_exp << frac_bits)
+ | raw_frac);
+}
+
+/* Converts the neutral format floating point number in FP into a
+ null-terminated human-readable hex string in OUTPUT. */
+static void
+assemble_hex (struct fp *fp, void *output)
+{
+ char buffer[32];
+ char *s = buffer;
+
+ if (fp->sign == NEGATIVE)
+ *s++ = '-';
+
+ switch (fp->class)
+ {
+ case FINITE:
+ normalize_and_round_fp (fp, 64);
+ normalize_hex_fp (fp);
+ assert (fp->fraction != 0);
+
+ *s++ = '.';
+ while (fp->fraction != 0)
+ {
+ *s++ = (fp->fraction >> 60)["0123456789abcdef"];
+ fp->fraction <<= 4;
+ }
+ if (fp->exponent != 0)
+ sprintf (s, "p%d", fp->exponent);
+ break;
+
+ case INFINITE:
+ strcpy (s, "Infinity");
+ break;
+
+ case NAN:
+ sprintf (s, "NaN:%016"PRIx64, fp->fraction);
+ break;
+
+ case ZERO:
+ strcpy (s, "0");
+ break;
+
+ case MISSING:
+ strcpy (buffer, "Missing");
+ break;
+
+ case LOWEST:
+ strcpy (buffer, "Lowest");
+ break;
+
+ case HIGHEST:
+ strcpy (buffer, "Highest");
+ break;
+
+ case RESERVED:
+ strcpy (s, "Reserved");
+ break;
+ }
+
+ strncpy (output, buffer, float_get_size (FLOAT_HEX));
+}
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+ Written by Ben Pfaff <blp@gnu.org>.
+
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef LIBPSPP_FLOAT_FORMAT_H
+#define LIBPSPP_FLOAT_FORMAT_H 1
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <libpspp/compiler.h>
+
+/* A floating-point format. */
+enum float_format
+ {
+ /* IEEE 754 formats. */
+ FLOAT_IEEE_SINGLE_LE, /* 32 bit, little endian. */
+ FLOAT_IEEE_SINGLE_BE, /* 32 bit, big endian. */
+ FLOAT_IEEE_DOUBLE_LE, /* 64 bit, little endian. */
+ FLOAT_IEEE_DOUBLE_BE, /* 64 bit, big endian. */
+
+ /* VAX formats. */
+ FLOAT_VAX_F, /* 32 bit VAX F format. */
+ FLOAT_VAX_D, /* 64 bit VAX D format. */
+ FLOAT_VAX_G, /* 64 bit VAX G format. */
+
+ /* IBM z architecture (390) hexadecimal formats. */
+ FLOAT_Z_SHORT, /* 32 bit format. */
+ FLOAT_Z_LONG, /* 64 bit format. */
+
+ /* Formats useful for testing. */
+ FLOAT_FP, /* Neutral intermediate format. */
+ FLOAT_HEX, /* C99 hexadecimal floating constant. */
+
+#ifdef FPREP_IEEE754
+#ifdef WORDS_BIGENDIAN
+ FLOAT_NATIVE_FLOAT = FLOAT_IEEE_SINGLE_BE,
+ FLOAT_NATIVE_DOUBLE = FLOAT_IEEE_DOUBLE_BE
+ FLOAT_NATIVE_32_BIT = FLOAT_IEEE_SINGLE_BE,
+ FLOAT_NATIVE_64_BIT = FLOAT_IEEE_DOUBLE_BE
+#else
+ FLOAT_NATIVE_FLOAT = FLOAT_IEEE_SINGLE_LE,
+ FLOAT_NATIVE_DOUBLE = FLOAT_IEEE_DOUBLE_LE,
+ FLOAT_NATIVE_32_BIT = FLOAT_IEEE_SINGLE_LE,
+ FLOAT_NATIVE_64_BIT = FLOAT_IEEE_DOUBLE_LE
+#endif
+#else
+#error Only IEEE-754 floating point currently supported for PSPP hosts.
+#endif
+ };
+
+void float_convert (enum float_format, const void *,
+ enum float_format, void *);
+
+double float_get_double (enum float_format, const void *);
+
+size_t float_get_size (enum float_format) PURE_FUNCTION;
+
+int float_identify (double expected_value, const void *, size_t,
+ enum float_format *best_guess);
+
+#endif /* float-format.h */
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+ Written by Ben Pfaff <blp@gnu.org>.
+
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include <config.h>
+
+#include <libpspp/integer-format.h>
+
+#include <assert.h>
+
+/* Returns true if FORMAT is a valid integer format. */
+static inline bool
+is_integer_format (enum integer_format format)
+{
+ return (format == INTEGER_MSB_FIRST
+ || format == INTEGER_LSB_FIRST
+ || format == INTEGER_VAX);
+}
+
+/* Converts the CNT bytes in INTEGER from SRC integer_format to DST
+ integer_format. */
+void
+integer_convert (enum integer_format src, const void *from,
+ enum integer_format dst, void *to,
+ size_t cnt)
+{
+ if (src != dst)
+ integer_put (integer_get (src, from, cnt), dst, to, cnt);
+ else if (from != to)
+ memcpy (to, from, cnt);
+}
+
+/* Returns the value of the CNT-byte integer at FROM, which is in
+ the given FORMAT. */
+uint64_t
+integer_get (enum integer_format format, const void *from_, size_t cnt)
+{
+ const uint8_t *from = from_;
+ uint64_t value = 0;
+ size_t i;
+
+ assert (is_integer_format (format));
+ assert (cnt < 8);
+
+ switch (format)
+ {
+ case INTEGER_MSB_FIRST:
+ for (i = 0; i < cnt; i++)
+ value = (value << 8) | from[i];
+ break;
+ case INTEGER_LSB_FIRST:
+ for (i = 0; i < cnt; i++)
+ value = (value << 8) | from[cnt - i - 1];
+ break;
+ case INTEGER_VAX:
+ for (i = 0; i < (cnt & ~1); i++)
+ value = (value << 8) | from[i ^ 1];
+ if (cnt & 1)
+ value = (value << 8) | from[cnt - 1];
+ break;
+ }
+
+ return value;
+}
+
+/* Stores VALUE as a CNT-byte integer at TO, in the given
+ FORMAT. */
+void
+integer_put (uint64_t value, enum integer_format format, void *to_, size_t cnt)
+{
+ uint8_t *to = to_;
+ size_t i;
+
+ assert (is_integer_format (format));
+ assert (cnt < 8);
+
+ value <<= 8 * (8 - cnt);
+
+ switch (format)
+ {
+ case INTEGER_MSB_FIRST:
+ for (i = 0; i < cnt; i++)
+ {
+ to[i] = value >> 56;
+ value <<= 8;
+ }
+ break;
+ case INTEGER_LSB_FIRST:
+ for (i = 0; i < cnt; i++)
+ {
+ to[cnt - i - 1] = value >> 56;
+ value <<= 8;
+ }
+ break;
+ case INTEGER_VAX:
+ for (i = 0; i < (cnt & ~1); i++)
+ {
+ to[i ^ 1] = value >> 56;
+ value <<= 8;
+ }
+ if (cnt & 1)
+ to[cnt - 1] = value >> 56;
+ break;
+ }
+}
+
+/* Returns true if bytes with index IDX1 and IDX2 in VALUE differ
+ in value. */
+static inline bool
+bytes_differ (uint64_t value, unsigned int idx1, unsigned int idx2)
+{
+ uint8_t byte1 = value >> (idx1 * 8);
+ uint8_t byte2 = value >> (idx2 * 8);
+ return byte1 != byte2;
+}
+
+/* Attempts to identify the integer format in which the LENGTH
+ bytes in INTEGER represent the given EXPECTED_VALUE. Returns
+ true if successful, false otherwise. On success, stores the
+ format in *FORMAT. */
+bool
+integer_identify (uint64_t expected_value, const void *integer, size_t length,
+ enum integer_format *format)
+{
+ /* Odd-length integers are confusing. */
+ assert (length % 2 == 0);
+
+ /* LENGTH must be greater than 2 because VAX format is
+ equivalent to little-endian for 2-byte integers. */
+ assert (length > 2);
+
+ /* EXPECTED_VALUE must contain different byte values, because
+ otherwise all formats are identical. */
+ assert (bytes_differ (expected_value, 0, 1)
+ || bytes_differ (expected_value, 0, 2)
+ || bytes_differ (expected_value, 0, 3)
+ || (length > 4
+ && (bytes_differ (expected_value, 0, 4)
+ || bytes_differ (expected_value, 0, 5)))
+ || (length > 6
+ && (bytes_differ (expected_value, 0, 6)
+ || bytes_differ (expected_value, 0, 7))));
+
+ if (integer_get (INTEGER_MSB_FIRST, integer, length) == expected_value)
+ *format = INTEGER_MSB_FIRST;
+ else if (integer_get (INTEGER_LSB_FIRST, integer, length) == expected_value)
+ *format = INTEGER_LSB_FIRST;
+ else if (integer_get (INTEGER_VAX, integer, length) == expected_value)
+ *format = INTEGER_VAX;
+ else
+ return false;
+
+ return true;
+}
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+ Written by Ben Pfaff <blp@gnu.org>.
+
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef LIBPSPP_INTEGER_FORMAT_H
+#define LIBPSPP_INTEGER_FORMAT_H 1
+
+#include <byteswap.h>
+#include <stdint.h>
+
+#include <libpspp/str.h>
+
+/* An integer format. */
+enum integer_format
+ {
+ INTEGER_MSB_FIRST, /* Big-endian: MSB at lowest address. */
+ INTEGER_LSB_FIRST, /* Little-endian: LSB at lowest address. */
+ INTEGER_VAX, /* VAX-endian: little-endian 16-bit words
+ in big-endian order. */
+
+ /* Native endianness. */
+#ifdef WORDS_BIGENDIAN
+ INTEGER_NATIVE = INTEGER_MSB_FIRST
+#else
+ INTEGER_NATIVE = INTEGER_LSB_FIRST
+#endif
+ };
+
+void integer_convert (enum integer_format, const void *,
+ enum integer_format, void *,
+ size_t);
+uint64_t integer_get (enum integer_format, const void *, size_t);
+void integer_put (uint64_t, enum integer_format, void *, size_t);
+
+bool integer_identify (uint64_t expected_value, const void *, size_t,
+ enum integer_format *);
+
+#endif /* libpspp/integer-format.h */
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+ Written by Ben Pfaff <blp@gnu.org>.
+
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include <config.h>
+
+#include <libpspp/legacy-encoding.h>
+
+#include "str.h"
+
+static const char ascii_to_ebcdic[256];
+static const char ebcdic_to_ascii[256];
+
+void
+legacy_recode (enum legacy_encoding from, const char *src,
+ enum legacy_encoding to, char *dst,
+ size_t size)
+{
+ if (from != to)
+ {
+ const char *table;
+ size_t i;
+
+ table = from == LEGACY_ASCII ? ascii_to_ebcdic : ebcdic_to_ascii;
+ for (i = 0; i < size; i++)
+ dst[i] = table[(unsigned char) src[i]];
+ }
+ else
+ {
+ if (src != dst)
+ memcpy (dst, src, size);
+ }
+}
+
+char
+legacy_to_native (enum legacy_encoding from, char c)
+{
+ legacy_recode (from, &c, LEGACY_NATIVE, &c, 1);
+ return c;
+}
+
+char
+legacy_from_native (enum legacy_encoding to, char c)
+{
+ legacy_recode (LEGACY_NATIVE, &c, to, &c, 1);
+ return c;
+}
+
+static const char ascii_to_ebcdic[256] =
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f,
+ 0x16, 0x05, 0x25, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26,
+ 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d,
+ 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f,
+ 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
+ 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+ 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d,
+ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
+ 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
+ 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0x5f, 0x07,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x15, 0x06, 0x17,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x1b,
+ 0x30, 0x31, 0x1a, 0x33, 0x34, 0x35, 0x36, 0x08,
+ 0x38, 0x39, 0x3a, 0x3b, 0x04, 0x14, 0x3e, 0xe1,
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c, 0x8d,
+ 0x8e, 0x8f, 0x90, 0x6a, 0x9b, 0x9c, 0x9d, 0x9e,
+ 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0x4a, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xa1, 0xbe, 0xbf,
+ 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xda, 0xdb,
+ 0xdc, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xec, 0xed,
+ 0xee, 0xef, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+ };
+
+static const char ebcdic_to_ascii[256] =
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x9c, 0x09, 0x86, 0x7f,
+ 0x97, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x9d, 0x85, 0x08, 0x87,
+ 0x18, 0x19, 0x92, 0x8f, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x0a, 0x17, 0x1b,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07,
+ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04,
+ 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a,
+ 0x20, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
+ 0xa7, 0xa8, 0xd5, 0x2e, 0x3c, 0x28, 0x2b, 0x7c,
+ 0x26, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x7e,
+ 0x2d, 0x2f, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xcb, 0x2c, 0x25, 0x5f, 0x3e, 0x3f,
+ 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1,
+ 0xc2, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22,
+ 0xc3, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
+ 0xca, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+ 0x71, 0x72, 0x5e, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
+ 0xd1, 0xe5, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7a, 0xd2, 0xd3, 0xd4, 0x5b, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0x5d, 0xe6, 0xe7,
+ 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed,
+ 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+ 0x51, 0x52, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3,
+ 0x5c, 0x9f, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5a, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+ };
+
--- /dev/null
+/* PSPP - computes sample statistics.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+ Written by Ben Pfaff <blp@gnu.org>.
+
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef LIBPSPP_LEGACY_ENCODING
+#define LIBPSPP_LEGACY_ENCODING 1
+
+#include <stddef.h>
+#include <libpspp/compiler.h>
+
+/* A legacy character encoding.
+ This exists only to handle the specific legacy EBCDIC-to-ASCII
+ recoding that MODE=360 file handles perform. */
+enum legacy_encoding
+ {
+ LEGACY_ASCII, /* ASCII or similar character set. */
+ LEGACY_EBCDIC, /* IBM EBCDIC character set. */
+
+ /* Native character set. */
+#if 'A' == 0x41
+ LEGACY_NATIVE = LEGACY_ASCII
+#elif 'A' == 0xc1
+ LEGACY_NATIVE = LEGACY_EBCDIC
+#else
+#error Cannot detect native character set.
+#endif
+ };
+
+void legacy_recode (enum legacy_encoding, const char *src,
+ enum legacy_encoding, char *dst, size_t);
+char legacy_to_native (enum legacy_encoding from, char) PURE_FUNCTION;
+char legacy_from_native (enum legacy_encoding to, char) PURE_FUNCTION;
+
+#endif /* libpspp/legacy-encoding.h */
+Thu Oct 26 20:20:39 2006 Ben Pfaff <blp@gnu.org>
+
+ * automake.mk: Add tests/formats/float-format.sh.
+
+ * tests/formats/float-format.sh: New test.
+
Sat Oct 7 11:06:59 WST 2006 John Darrington <john@darrington.wattle.id.au>
* command/rank.sh: New file
tests/command/use.sh \
tests/command/very-long-strings.sh \
tests/command/weight.sh \
+ tests/formats/float-format.sh \
tests/bugs/agg_crash.sh \
tests/bugs/agg-crash-2.sh \
tests/bugs/alpha-freq.sh \
--- /dev/null
+#! /bin/sh
+
+# Tests floating-point format conversions.
+
+TEMPDIR=/tmp/pspp-tst-$$
+
+# ensure that top_builddir are absolute
+if [ -z "$top_builddir" ] ; then top_builddir=. ; fi
+if [ -z "$top_srcdir" ] ; then top_srcdir=. ; fi
+top_builddir=`cd $top_builddir; pwd`
+PSPP=$top_builddir/src/ui/terminal/pspp
+
+# ensure that top_srcdir is absolute
+top_srcdir=`cd $top_srcdir; pwd`
+
+STAT_CONFIG_PATH=$top_srcdir/config
+export STAT_CONFIG_PATH
+
+
+cleanup()
+{
+ cd /
+ rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+ echo $activity
+ echo FAILED
+ cleanup;
+ exit 1;
+}
+
+
+no_result()
+{
+ echo $activity
+ echo NO RESULT;
+ cleanup;
+ exit 2;
+}
+
+pass()
+{
+ cleanup;
+ exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+activity="create test program"
+sed -n \
+ -e 's/#.*//' \
+ -e 's/^[ ]*//' \
+ -e 's/[ ]*$//' \
+ -e '/^$/d' \
+ -e 's/^\(.*\)$/DEBUG FLOAT FORMAT \1./p' \
+ > $TEMPDIR/float-format.pspp <<'EOF'
+# Each of the tests below checks that conversion between
+# floating-point formats works correctly. Comparisons that use ==
+# require that conversion from any format on the line to any other
+# format on the line work losslessly. Comparisons that use => only
+# check that conversions work losslessly in the given direction.
+
+# Key to format names:
+# isl: IEEE single-precision, little endian
+# isb: IEEE single-precision, big endian
+# idl: IEEE double-precision, little endian
+# idb: IEEE double-precision, big endian
+# vf: VAX F
+# vd: VAX D
+# vg: VAX G
+# zs: Z architecture short
+# zl: Z architecture long
+# x: hexadecimal digits
+
+# IEEE special values.
+ 0 == isb(x'00000000')
+-0 == isb(x'80000000')
+x('Infinity') == isb(x'7f800000')
+x('-Infinity') == isb(x'ff800000')
+x('NaN:') => isb(x'7f800001') # NaN requires nonzero fraction.
+x('NaN:e000000000000000') == isb(x'7ff00000') == idb(x'7ffe000000000000')
+x('NaN:5a5a5e0000000000') == isb(x'7fad2d2f') == idb(x'7ff5a5a5e0000000')
+x('NaN:975612abcdef4000') == idb(x'7ff975612abcdef4')
+x('-NaN:e000000000000000') == isb(x'fff00000') == idb(x'fffe000000000000')
+x('-NaN:5a5a5e0000000000') == isb(x'ffad2d2f') == idb(x'fff5a5a5e0000000')
+x('-NaN:975612abcdef4000') == idb(x'fff975612abcdef4')
+
+# PSPP special values.
+x('Missing') == isb(x'ff7fffff') == idb(x'ffefffffffffffff') == isl(x'ffff7fff') == idl(x'ffffffffffffefff') == vf(x'ffffffff') == vd(x'ffffffffffffffff') == vg(x'ffffffffffffffff') == zs(x'ffffffff') == zl(x'ffffffffffffffff')
+x('Lowest') == isb(x'ff7ffffe') == idb(x'ffeffffffffffffe') == isl(x'feff7fff') == idl(x'feffffffffffefff') == vf(x'fffffeff') == vd(x'fffffeffffffffff') == vg(x'fffffeffffffffff') == zs(x'fffffffe') == zl(x'fffffffffffffffe')
+x('Highest') == isb(x'7f7fffff') == idb(x'7fefffffffffffff') == isl(x'ffff7f7f') == idl(x'ffffffffffffef7f') == vf(x'ff7fffff') == vd(x'ffffffffff7fffff') == vg(x'ffffffffff7fffff') == zs(x'7fffffff') == zl(x'7fffffffffffffff')
+
+# From Wikipedia.
+0.15625 == isb(b'00111110001000000000000000000000')
+-118.625 == isb(b'11000010111011010100000000000000')
+
+# http://www.psc.edu/general/software/packages/ieee/ieee.html
+x('NaN:0400000000000000') == isb(b'01111111100000100000000000000000')
+x('-NaN:2225540000000000') == isb(b'11111111100100010001001010101010')
+2 == isb(b'01000000000000000000000000000000')
+6.5 == isb(b'01000000110100000000000000000000')
+-6.5 == isb(b'11000000110100000000000000000000')
+x('.4p-124') == isb(b'00000000100000000000000000000000')
+x('.2p-124') == isb(b'00000000010000000000000000000000')
+
+# Using converter at http://babbage.cs.qc.edu/IEEE-754/Decimal.html
+# plus Emacs 'calc' to convert decimal to hexadecimal
+x('.7b74bc6a7ef9db23p8') => isb(x'42f6e979') # 123.456
+x('.7b74bc6a7ef9db23p8') => idb(x'405edd2f1a9fbe77')
+x('.817427d2d4642004p-12') => isb(x'39017428') # .0001234567
+x('.817427d2d4642004p-12') => idb(x'3f202e84fa5a8c84')
+x('.446c3b15f9926688p168') => isb(x'7f800000') # 1e50; overflow
+x('.446c3b15f9926688p168') => idb(x'4a511b0ec57e649a')
+
+# From multiple editions of the z/Architecture Principles of Operation
+# manual.
+ 1.0 == zs(x'41100000') == isb(x'3f800000')
+ 0.5 == zs(x'40800000') == isb(x'3f000000')
+ x('.4p-4') == zs(x'3f400000') == isb(x'3c800000')
+ 0 == zs(x'00000000') == isb(x'00000000')
+ -0 == zs(x'80000000') == isb(x'80000000')
+ -15 == zs(x'c1f00000') == isb(x'c1700000')
+# x('.ffffffp252') == zs(x'7fffffff')
+ x('.3b4p8') == zs(x'423b4000')
+ x('.1p-256') == zs(x'00100000')
+ x('.4p-124') == zs(x'21400000') == isb(x'00800000')
+ x('.8p-148') == zs(x'1b800000') == isb(x'00000001')
+# x('.ffffffp128') == zs(x'60ffffff') == isb(x'7f7fffff')
+ x('.1p-256') == zs(x'00100000')
+ x('.1p-256') => isb(x'00000000') # Underflow to zero.
+ x('.ffffffp248') == zs(x'7effffff')
+ x('.ffffffp248') => isb(x'7f800000') # Overflow to +Infinity.
+
+ x('.4p-1020') => zl(x'0000000000000000') # Underflow to zero.
+ x('.4p-1020') == idb(x'0010000000000000')
+ x('.4p-1072') => zl(x'0000000000000000') # Underflow to zero.
+ x('.4p-1072') => idb(x'0000000000000001')
+x('.fffffffffffff8p1024') => zl(x'7fffffffffffffff') # Overflow to maxval.
+x('.fffffffffffff8p1024') => idb(x'7fefffffffffffff')
+ x('.1p-256') == zl(x'0010000000000000') == idb(x'2fb0000000000000')
+ x('.ffffffffffffffp248') == zl(x'7effffffffffffff')
+ x('.ffffffffffffffp248') => idb(x'4f70000000000000') # Loses precision.
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+activity="run program"
+$SUPERVISOR $PSPP --testing-mode -o raw-ascii $TEMPDIR/float-format.pspp
+if [ $? -ne 0 ] ; then fail ; fi
+
+pass