comparison Modules/structmodule.c @ 28333:eec3fd164a7d 2.2

SF bug 705836: struct.pack of floats in non-native endian order pack_float, pack_double, save_float: All the routines for creating IEEE-format packed representations of floats and doubles simply ignored that rounding can (in rare cases) propagate out of a long string of 1 bits. At worst, the end-off carry can (by mistake) interfere with the exponent value, and then unpacking yields a result wrong by a factor of 2. In less severe cases, it can end up losing more low-order bits than intended, or fail to catch overflow *caused* by rounding.
author Tim Peters <tim.peters@gmail.com>
date Thu, 20 Mar 2003 18:31:20 +0000
parents 0b68d810a8be
children
comparison
equal deleted inserted replaced
28332:291b9d866ecf 28333:eec3fd164a7d
223 PyErr_SetString(PyExc_SystemError, 223 PyErr_SetString(PyExc_SystemError,
224 "frexp() result out of range"); 224 "frexp() result out of range");
225 return -1; 225 return -1;
226 } 226 }
227 227
228 if (e >= 128) { 228 if (e >= 128)
229 /* XXX 128 itself is reserved for Inf/NaN */ 229 goto Overflow;
230 PyErr_SetString(PyExc_OverflowError,
231 "float too large to pack with f format");
232 return -1;
233 }
234 else if (e < -126) { 230 else if (e < -126) {
235 /* Gradual underflow */ 231 /* Gradual underflow */
236 f = ldexp(f, 126 + e); 232 f = ldexp(f, 126 + e);
237 e = 0; 233 e = 0;
238 } 234 }
241 f -= 1.0; /* Get rid of leading 1 */ 237 f -= 1.0; /* Get rid of leading 1 */
242 } 238 }
243 239
244 f *= 8388608.0; /* 2**23 */ 240 f *= 8388608.0; /* 2**23 */
245 fbits = (long) floor(f + 0.5); /* Round */ 241 fbits = (long) floor(f + 0.5); /* Round */
242 assert(fbits <= 8388608);
243 if (fbits >> 23) {
244 /* The carry propagated out of a string of 23 1 bits. */
245 fbits = 0;
246 ++e;
247 if (e >= 255)
248 goto Overflow;
249 }
246 250
247 /* First byte */ 251 /* First byte */
248 *p = (s<<7) | (e>>1); 252 *p = (s<<7) | (e>>1);
249 p += incr; 253 p += incr;
250 254
259 /* Fourth byte */ 263 /* Fourth byte */
260 *p = fbits&0xFF; 264 *p = fbits&0xFF;
261 265
262 /* Done */ 266 /* Done */
263 return 0; 267 return 0;
268
269 Overflow:
270 PyErr_SetString(PyExc_OverflowError,
271 "float too large to pack with f format");
272 return -1;
264 } 273 }
265 274
266 static int 275 static int
267 pack_double(double x, /* The number to pack */ 276 pack_double(double x, /* The number to pack */
268 char *p, /* Where to pack the high order byte */ 277 char *p, /* Where to pack the high order byte */
294 PyErr_SetString(PyExc_SystemError, 303 PyErr_SetString(PyExc_SystemError,
295 "frexp() result out of range"); 304 "frexp() result out of range");
296 return -1; 305 return -1;
297 } 306 }
298 307
299 if (e >= 1024) { 308 if (e >= 1024)
300 /* XXX 1024 itself is reserved for Inf/NaN */ 309 goto Overflow;
301 PyErr_SetString(PyExc_OverflowError,
302 "float too large to pack with d format");
303 return -1;
304 }
305 else if (e < -1022) { 310 else if (e < -1022) {
306 /* Gradual underflow */ 311 /* Gradual underflow */
307 f = ldexp(f, 1022 + e); 312 f = ldexp(f, 1022 + e);
308 e = 0; 313 e = 0;
309 } 314 }
313 } 318 }
314 319
315 /* fhi receives the high 28 bits; flo the low 24 bits (== 52 bits) */ 320 /* fhi receives the high 28 bits; flo the low 24 bits (== 52 bits) */
316 f *= 268435456.0; /* 2**28 */ 321 f *= 268435456.0; /* 2**28 */
317 fhi = (long) floor(f); /* Truncate */ 322 fhi = (long) floor(f); /* Truncate */
323 assert(fhi < 268435456);
324
318 f -= (double)fhi; 325 f -= (double)fhi;
319 f *= 16777216.0; /* 2**24 */ 326 f *= 16777216.0; /* 2**24 */
320 flo = (long) floor(f + 0.5); /* Round */ 327 flo = (long) floor(f + 0.5); /* Round */
328 assert(flo <= 16777216);
329 if (flo >> 24) {
330 /* The carry propagated out of a string of 24 1 bits. */
331 flo = 0;
332 ++fhi;
333 if (fhi >> 28) {
334 /* And it also progagated out of the next 28 bits. */
335 fhi = 0;
336 ++e;
337 if (e >= 2047)
338 goto Overflow;
339 }
340 }
321 341
322 /* First byte */ 342 /* First byte */
323 *p = (s<<7) | (e>>4); 343 *p = (s<<7) | (e>>4);
324 p += incr; 344 p += incr;
325 345
351 *p = flo & 0xFF; 371 *p = flo & 0xFF;
352 p += incr; 372 p += incr;
353 373
354 /* Done */ 374 /* Done */
355 return 0; 375 return 0;
376
377 Overflow:
378 PyErr_SetString(PyExc_OverflowError,
379 "float too large to pack with d format");
380 return -1;
356 } 381 }
357 382
358 static PyObject * 383 static PyObject *
359 unpack_float(const char *p, /* Where the high order byte is */ 384 unpack_float(const char *p, /* Where the high order byte is */
360 int incr) /* 1 for big-endian; -1 for little-endian */ 385 int incr) /* 1 for big-endian; -1 for little-endian */