1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2006, 2010 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "libpspp/float-format.h"
25 #include "language/command.h"
26 #include "language/lexer/lexer.h"
27 #include "libpspp/assertion.h"
28 #include "libpspp/cast.h"
29 #include "libpspp/message.h"
30 #include "libpspp/str.h"
32 /* Maximum supported size of a floating-point number, in bytes. */
33 #define FP_MAX_SIZE 32
35 /* A floating-point number tagged with its representation. */
38 enum float_format format; /* Format. */
39 uint8_t data[FP_MAX_SIZE]; /* Representation. */
42 /* Associates a format name with its identifier. */
46 enum float_format format;
49 /* List of floating-point formats. */
50 static const struct assoc fp_formats[] =
52 {"ISL", FLOAT_IEEE_SINGLE_LE},
53 {"ISB", FLOAT_IEEE_SINGLE_BE},
54 {"IDL", FLOAT_IEEE_DOUBLE_LE},
55 {"IDB", FLOAT_IEEE_DOUBLE_BE},
59 {"ZS", FLOAT_Z_SHORT},
64 static const size_t format_cnt = sizeof fp_formats / sizeof *fp_formats;
66 /* Parses a floating-point format name into *FORMAT,
67 and returns success. */
69 parse_float_format (struct lexer *lexer, enum float_format *format)
73 for (i = 0; i < format_cnt; i++)
74 if (lex_match_id (lexer, fp_formats[i].name))
76 *format = fp_formats[i].format;
79 lex_error (lexer, "expecting floating-point format identifier");
83 /* Returns the name for the given FORMAT. */
85 get_float_format_name (enum float_format format)
89 for (i = 0; i < format_cnt; i++)
90 if (fp_formats[i].format == format)
91 return fp_formats[i].name;
96 /* Returns the integer value of (hex) digit C. */
112 case 'a': case 'A': return 10;
113 case 'b': case 'B': return 11;
114 case 'c': case 'C': return 12;
115 case 'd': case 'D': return 13;
116 case 'e': case 'E': return 14;
117 case 'f': case 'F': return 15;
118 default: return INT_MAX;
122 /* Parses a number in the form FORMAT(STRING), where FORMAT is
123 the name of the format and STRING gives the number's
124 representation. Also supports ordinary floating-point numbers
125 written in decimal notation. Returns success. */
127 parse_fp (struct lexer *lexer, struct fp *fp)
129 memset (fp, 0, sizeof *fp);
130 if (lex_is_number (lexer))
132 double number = lex_number (lexer);
133 fp->format = FLOAT_NATIVE_DOUBLE;
134 memcpy (fp->data, &number, sizeof number);
137 else if (lex_token (lexer) == T_ID)
141 if (!parse_float_format (lexer, &fp->format)
142 || !lex_force_match (lexer, T_LPAREN)
143 || !lex_force_string (lexer))
146 s = lex_tokss (lexer);
147 if (fp->format != FLOAT_HEX)
151 if (s.length != float_get_size (fp->format) * 2)
153 msg (SE, "%zu-byte string needed but %zu-byte string "
154 "supplied.", float_get_size (fp->format), s.length);
157 assert (s.length / 2 <= sizeof fp->data);
158 for (i = 0; i < s.length / 2; i++)
160 int hi = digit_value (s.string[i * 2]);
161 int lo = digit_value (s.string[i * 2 + 1]);
163 if (hi >= 16 || lo >= 16)
165 msg (SE, "Invalid hex digit in string.");
169 fp->data[i] = hi * 16 + lo;
174 if (s.length >= sizeof fp->data)
176 msg (SE, "Hexadecimal floating constant too long.");
179 memcpy (fp->data, s.string, s.length);
183 if (!lex_force_match (lexer, T_RPAREN))
188 lex_error (lexer, NULL);
194 /* Renders SRC, which contains SRC_SIZE bytes of a floating-point
195 number in the given FORMAT, as relatively human-readable
196 null-terminated string in the DST_SIZE bytes in DST. DST_SIZE
197 must be be large enough to hold the output. */
199 make_printable (enum float_format format, const void *src_, size_t src_size,
200 char *dst, size_t dst_size)
202 assert (dst_size >= 2 * src_size + 1);
203 if (format != FLOAT_HEX)
205 const uint8_t *src = src_;
206 while (src_size-- > 0)
208 sprintf (dst, "%02x", *src++);
214 strncpy (dst, src_, src_size + 1);
217 /* Checks that RESULT is identical to TO.
218 If so, returns false.
219 If not, issues a helpful error message that includes the given
220 CONVERSION_TYPE and the value that was converted FROM, and
223 mismatch (const struct fp *from, const struct fp *to, char *result,
224 const char *conversion_type)
226 size_t to_size = float_get_size (to->format);
227 if (!memcmp (to->data, result, to_size))
231 size_t from_size = float_get_size (from->format);
232 char original[FP_MAX_SIZE * 2 + 1];
233 char expected[FP_MAX_SIZE * 2 + 1];
234 char actual[FP_MAX_SIZE * 2 + 1];
235 make_printable (from->format, from->data, from_size, original,
237 make_printable (to->format, to->data, to_size, expected,
239 make_printable (to->format, result, to_size, actual, sizeof actual);
240 msg (SE, "%s conversion of %s from %s to %s should have produced %s "
241 "but actually produced %s.",
243 original, get_float_format_name (from->format),
244 get_float_format_name (to->format), expected,
250 /* Checks that converting FROM into the format of TO yields
251 exactly the data in TO. */
253 verify_conversion (const struct fp *from, const struct fp *to)
255 char tmp1[FP_MAX_SIZE], tmp2[FP_MAX_SIZE];
257 /* First try converting directly. */
258 float_convert (from->format, from->data, to->format, tmp1);
259 if (mismatch (from, to, tmp1, "Direct"))
262 /* Then convert via FLOAT_FP to prevent short-circuiting that
263 float_convert() does for some conversions (e.g. little<->big
264 endian for IEEE formats). */
265 float_convert (from->format, from->data, FLOAT_FP, tmp1);
266 float_convert (FLOAT_FP, tmp1, to->format, tmp2);
267 if (mismatch (from, to, tmp2, "Indirect"))
273 /* Executes the DEBUG FLOAT FORMAT command. */
275 cmd_debug_float_format (struct lexer *lexer, struct dataset *ds UNUSED)
279 bool bijective = false;
284 if (fp_cnt >= sizeof fp / sizeof *fp)
286 msg (SE, "Too many values in single command.");
289 if (!parse_fp (lexer, &fp[fp_cnt++]))
292 if (lex_token (lexer) == T_ENDCMD && fp_cnt > 1)
294 else if (!lex_force_match (lexer, T_EQUALS))
299 if (lex_match (lexer, T_EQUALS))
301 else if (lex_match (lexer, T_GT))
305 lex_error (lexer, NULL);
311 if ((bijective && !lex_force_match (lexer, T_EQUALS))
312 || (!bijective && !lex_force_match (lexer, T_GT)))
322 for (i = 0; i < fp_cnt; i++)
323 for (j = 0; j < fp_cnt; j++)
324 if (!verify_conversion (&fp[i], &fp[j]))
331 for (i = 1; i < fp_cnt; i++)
332 if (!verify_conversion (&fp[i - 1], &fp[i]))
336 return ok ? CMD_SUCCESS : CMD_FAILURE;