1 /* PSPP - computes sample statistics.
2 Copyright (C) 2006 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 #include <libpspp/float-format.h>
27 #include <language/command.h>
28 #include <language/lexer/lexer.h>
29 #include <libpspp/assertion.h>
30 #include <libpspp/message.h>
31 #include <libpspp/str.h>
33 #define _(msgid) gettext (msgid)
35 /* Maximum supported size of a floating-point number, in bytes. */
36 #define FP_MAX_SIZE 32
38 /* A floating-point number tagged with its representation. */
41 enum float_format format; /* Format. */
42 uint8_t data[FP_MAX_SIZE]; /* Representation. */
45 /* Associates a format name with its identifier. */
49 enum float_format format;
52 /* List of floating-point formats. */
53 static const struct assoc fp_formats[] =
55 {"ISL", FLOAT_IEEE_SINGLE_LE},
56 {"ISB", FLOAT_IEEE_SINGLE_BE},
57 {"IDL", FLOAT_IEEE_DOUBLE_LE},
58 {"IDB", FLOAT_IEEE_DOUBLE_BE},
62 {"ZS", FLOAT_Z_SHORT},
67 static const size_t format_cnt = sizeof fp_formats / sizeof *fp_formats;
69 /* Parses a floating-point format name into *FORMAT,
70 and returns success. */
72 parse_float_format (struct lexer *lexer, enum float_format *format)
76 for (i = 0; i < format_cnt; i++)
77 if (lex_match_id (lexer, fp_formats[i].name))
79 *format = fp_formats[i].format;
82 lex_error (lexer, "expecting floating-point format identifier");
86 /* Returns the name for the given FORMAT. */
88 get_float_format_name (enum float_format format)
92 for (i = 0; i < format_cnt; i++)
93 if (fp_formats[i].format == format)
94 return fp_formats[i].name;
99 /* Parses a number in the form FORMAT(STRING), where FORMAT is
100 the name of the format and STRING gives the number's
101 representation. Also supports ordinary floating-point numbers
102 written in decimal notation. Returns success. */
104 parse_fp (struct lexer *lexer, struct fp *fp)
106 if (lex_is_number (lexer))
108 double number = lex_number (lexer);
109 fp->format = FLOAT_NATIVE_DOUBLE;
110 memcpy (fp->data, &number, sizeof number);
113 else if (lex_token (lexer) == T_ID)
117 if (!parse_float_format (lexer, &fp->format)
118 || !lex_force_match (lexer, '(')
119 || !lex_force_string (lexer))
122 length = ds_length (lex_tokstr (lexer));
123 if (fp->format != FLOAT_HEX)
125 if (length != float_get_size (fp->format))
127 msg (SE, _("%d-byte string needed but %d-byte string supplied."),
128 (int) float_get_size (fp->format), (int) length);
131 assert (length <= sizeof fp->data);
132 memcpy (fp->data, ds_data (lex_tokstr (lexer)), length);
136 if (length >= sizeof fp->data)
138 msg (SE, _("Hexadecimal floating constant too long."));
141 strncpy ((char *) fp->data, ds_cstr (lex_tokstr (lexer)), sizeof fp->data);
145 if (!lex_force_match (lexer, ')'))
150 lex_error (lexer, NULL);
156 /* Renders SRC, which contains SRC_SIZE bytes of a floating-point
157 number in the given FORMAT, as relatively human-readable
158 null-terminated string in the DST_SIZE bytes in DST. DST_SIZE
159 must be be large enough to hold the output. */
161 make_printable (enum float_format format, const void *src_, size_t src_size,
162 char *dst, size_t dst_size)
164 assert (dst_size >= 2 * src_size + 1);
165 if (format != FLOAT_HEX)
167 const uint8_t *src = src_;
168 while (src_size-- > 0)
170 sprintf (dst, "%02x", *src++);
176 strncpy (dst, src_, src_size + 1);
179 /* Checks that RESULT is identical to TO.
180 If so, returns false.
181 If not, issues a helpful error message that includes the given
182 CONVERSION_TYPE and the value that was converted FROM, and
185 mismatch (const struct fp *from, const struct fp *to, char *result,
186 const char *conversion_type)
188 size_t to_size = float_get_size (to->format);
189 if (!memcmp (to->data, result, to_size))
193 size_t from_size = float_get_size (from->format);
194 char original[FP_MAX_SIZE * 2 + 1];
195 char expected[FP_MAX_SIZE * 2 + 1];
196 char actual[FP_MAX_SIZE * 2 + 1];
197 make_printable (from->format, from->data, from_size, original,
199 make_printable (to->format, to->data, to_size, expected,
201 make_printable (to->format, result, to_size, actual, sizeof actual);
203 _("%s conversion of %s from %s to %s should have produced %s "
204 "but actually produced %s."),
206 original, get_float_format_name (from->format),
207 get_float_format_name (to->format), expected,
213 /* Checks that converting FROM into the format of TO yields
214 exactly the data in TO. */
216 verify_conversion (const struct fp *from, const struct fp *to)
218 char tmp1[FP_MAX_SIZE], tmp2[FP_MAX_SIZE];
220 /* First try converting directly. */
221 float_convert (from->format, from->data, to->format, tmp1);
222 if (mismatch (from, to, tmp1, "Direct"))
225 /* Then convert via FLOAT_FP to prevent short-circuiting that
226 float_convert() does for some conversions (e.g. little<->big
227 endian for IEEE formats). */
228 float_convert (from->format, from->data, FLOAT_FP, tmp1);
229 float_convert (FLOAT_FP, tmp1, to->format, tmp2);
230 if (mismatch (from, to, tmp2, "Indirect"))
236 /* Executes the DEBUG FLOAT FORMAT command. */
238 cmd_debug_float_format (struct lexer *lexer, struct dataset *ds UNUSED)
242 bool bijective = false;
247 if (fp_cnt >= sizeof fp / sizeof *fp)
249 msg (SE, _("Too many values in single command."));
252 if (!parse_fp (lexer, &fp[fp_cnt++]))
255 if (lex_token (lexer) == '.' && fp_cnt > 1)
257 else if (!lex_force_match (lexer, '='))
262 if (lex_match (lexer, '='))
264 else if (lex_match (lexer, T_GT))
268 lex_error (lexer, NULL);
274 if ((bijective && !lex_force_match (lexer, '='))
275 || (!bijective && !lex_force_match (lexer, T_GT)))
285 for (i = 0; i < fp_cnt; i++)
286 for (j = 0; j < fp_cnt; j++)
287 if (!verify_conversion (&fp[i], &fp[j]))
294 for (i = 1; i < fp_cnt; i++)
295 if (!verify_conversion (&fp[i - 1], &fp[i]))
299 return ok ? CMD_SUCCESS : CMD_FAILURE;