Mercurial > cpython-lru_cache-weakref
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 */ |
