From: Ben Pfaff Date: Sat, 28 Oct 2006 04:01:54 +0000 (+0000) Subject: Add routines for integer byte order conversions, floating point format X-Git-Tag: v0.6.0~719 X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f89de8c330e8f82f0e7195c4d35588cfcbdd02fc;p=pspp-builds.git Add routines for integer byte order conversions, floating point format and byte order conversions, and ASCII<->EBCDIC conversion. Also add an extensive floating point format conversion test. The ASCII<->EBCDIC conversion used by SPSS does not correspond exactly to any found in GNU libiconv, hence the need for a separate library. These routines are a small part of a larger change set that I'm breaking up for gradual submission. Thus, they aren't used by much yet. --- diff --git a/Smake b/Smake index bc331007..a020ee86 100644 --- a/Smake +++ b/Smake @@ -8,6 +8,7 @@ GNULIB_MODULES = \ alloca \ alloca-opt \ assert \ + byteswap \ c-ctype \ c-strtod \ exit \ diff --git a/src/language/ChangeLog b/src/language/ChangeLog index a0dd1a08..2d49a13b 100644 --- a/src/language/ChangeLog +++ b/src/language/ChangeLog @@ -1,3 +1,7 @@ +Thu Oct 26 20:19:00 2006 Ben Pfaff + + * command.def: Add DEBUG FLOAT FORMAT. + Fri Oct 20 10:59:06 WST 2006 John Darrington * command.def: Added additional unimpl commands. diff --git a/src/language/command.def b/src/language/command.def index cca13f0a..68acb3b9 100644 --- a/src/language/command.def +++ b/src/language/command.def @@ -131,6 +131,7 @@ DEF_CMD (S_ANY, F_TESTING, "DEBUG CASEFILE", cmd_debug_casefile) 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. */ diff --git a/src/language/lexer/ChangeLog b/src/language/lexer/ChangeLog index 90eb0e21..7172c1b8 100644 --- a/src/language/lexer/ChangeLog +++ b/src/language/lexer/ChangeLog @@ -1,3 +1,9 @@ +Thu Oct 26 20:18:03 2006 Ben Pfaff + + * 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 * format-parser.h: New file. Moved prototypes of format-parser.c diff --git a/src/language/lexer/lexer.c b/src/language/lexer/lexer.c index 452eb7e7..9f9e4338 100644 --- a/src/language/lexer/lexer.c +++ b/src/language/lexer/lexer.c @@ -1067,6 +1067,9 @@ convert_numeric_string_to_char_string (enum string_type type) static int parse_string (enum string_type type) { + if (type != CHARACTER_STRING) + prog++; + /* Accumulate the entire string, joining sections indicated by + signs. */ for (;;) @@ -1160,24 +1163,6 @@ finish: 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; } diff --git a/src/language/tests/ChangeLog b/src/language/tests/ChangeLog index 605b8cc5..ba09d323 100644 --- a/src/language/tests/ChangeLog +++ b/src/language/tests/ChangeLog @@ -1,3 +1,9 @@ +Thu Oct 26 20:19:19 2006 Ben Pfaff + + * automake.mk: Add float-format.c. + + * float-format.c: New file. + Sat Oct 14 16:21:45 2006 Ben Pfaff * casefile-test.c: (test_casereader_clone) Free cases that we diff --git a/src/language/tests/automake.mk b/src/language/tests/automake.mk index 28e37d5c..a5317135 100644 --- a/src/language/tests/automake.mk +++ b/src/language/tests/automake.mk @@ -5,4 +5,5 @@ noinst_LIBRARIES += src/language/tests/libtests.a 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 diff --git a/src/language/tests/float-format.c b/src/language/tests/float-format.c new file mode 100644 index 00000000..c2ca32f0 --- /dev/null +++ b/src/language/tests/float-format.c @@ -0,0 +1,300 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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 + +#include + +#include "gettext.h" +#include + +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/libpspp/ChangeLog b/src/libpspp/ChangeLog index f1e04802..c237dfb7 100644 --- a/src/libpspp/ChangeLog +++ b/src/libpspp/ChangeLog @@ -1,3 +1,19 @@ +Thu Oct 26 20:19:50 2006 Ben Pfaff + + * 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 * hash.c hash.h: Added hsh_create_pool, a hash which uses a pool diff --git a/src/libpspp/automake.mk b/src/libpspp/automake.mk index 7abd32cc..da01e2d5 100644 --- a/src/libpspp/automake.mk +++ b/src/libpspp/automake.mk @@ -10,15 +10,21 @@ src_libpspp_libpspp_a_SOURCES = \ 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 \ diff --git a/src/libpspp/float-format.c b/src/libpspp/float-format.c new file mode 100644 index 00000000..5e56da44 --- /dev/null +++ b/src/libpspp/float-format.c @@ -0,0 +1,1078 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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 + +#include + +#include +#include +#include + +#include +#include +#include + +#include "error.h" +#include + +/* 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; +} + +/* 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)); +} + +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; + } + } + } +} + +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)); +} diff --git a/src/libpspp/float-format.h b/src/libpspp/float-format.h new file mode 100644 index 00000000..14ef2c42 --- /dev/null +++ b/src/libpspp/float-format.h @@ -0,0 +1,76 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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 +#include +#include + +/* 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 */ diff --git a/src/libpspp/integer-format.c b/src/libpspp/integer-format.c new file mode 100644 index 00000000..ca3455a0 --- /dev/null +++ b/src/libpspp/integer-format.c @@ -0,0 +1,169 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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 + +#include + +#include + +/* 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; +} diff --git a/src/libpspp/integer-format.h b/src/libpspp/integer-format.h new file mode 100644 index 00000000..c3ca92e2 --- /dev/null +++ b/src/libpspp/integer-format.h @@ -0,0 +1,53 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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 +#include + +#include + +/* 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 */ diff --git a/src/libpspp/legacy-encoding.c b/src/libpspp/legacy-encoding.c new file mode 100644 index 00000000..bcc1deaf --- /dev/null +++ b/src/libpspp/legacy-encoding.c @@ -0,0 +1,135 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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 + +#include + +#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, + }; + diff --git a/src/libpspp/legacy-encoding.h b/src/libpspp/legacy-encoding.h new file mode 100644 index 00000000..8cb8cce9 --- /dev/null +++ b/src/libpspp/legacy-encoding.h @@ -0,0 +1,49 @@ +/* PSPP - computes sample statistics. + Copyright (C) 2006 Free Software Foundation, Inc. + Written by Ben Pfaff . + + 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 +#include + +/* 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 */ diff --git a/tests/ChangeLog b/tests/ChangeLog index de03c0db..8cc076d6 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,9 @@ +Thu Oct 26 20:20:39 2006 Ben Pfaff + + * 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 * command/rank.sh: New file diff --git a/tests/automake.mk b/tests/automake.mk index 76494efe..2527c836 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -57,6 +57,7 @@ TESTS = \ 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 \ diff --git a/tests/formats/float-format.sh b/tests/formats/float-format.sh new file mode 100755 index 00000000..4ce1f391 --- /dev/null +++ b/tests/formats/float-format.sh @@ -0,0 +1,154 @@ +#! /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