1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 ''' ieeefloat a set of functions for dealing with ieee-754 float objects
25
26 On most platforms Python uses ieee-754 double objects of length 64 bits to
27 represent floats (some architectures such as older crays and vaxes don't).
28 Thus an ieee-754 double is the implementation of a python float object on
29 most platforms.
30
31 ieee-74 uses special bit patterns to represent the following states or classes
32 of ieee floating point numbers (ieee-class)
33 +-nan - not a number (e.g. 0.0/0.0)
34 inf - positive or negative infinity (1.0/0.0)
35 +-zero - zero maybe positive or negative under ieee-754
36
37 this module provides functions for working with python floats and their
38 special values, if they contain ieee-754 formatted values. Specifically
39 - pack and unpack a list of bytes representing an ieee-754 double to a python
40 float (takes care of little endian/big endian issues)
41 - get the sign bit of a python float
42 - check the ordering of python floats allowing for nans (nans cannot normally
43 be compared)
44 - check if a value is finite (as opposed to nan or inf)
45 - copy the sign of one float to another irrespective of if it's ieee-class
46 - check if a float is denormalised (and might be about to underflow)
47 - check the ieee-class of a python float (nan, pos-inf, neg-inf,pos-zero,
48 neg-zero,...)
49 - check that the current python float implmentations uses ieee-754 doubles
50
51 It also provides constants containg specific bit patterns for nan and +-inf as
52 these values cannot be generated from strings via the constructor float(x)
53 with some compiler implementations (typically older microsoft windows compilers)
54
55 As a convenience the names of functions and constants conform to those defined
56 in the draft python PEP 754 'IEEE 754 Floating Point Special Values'
57
58 notes:
59 1. binary data is docuemented as binary strings e.g. 0xF0 = 0b11110000
60 2. the module doesn't support all the functions recommened by ieee-754,
61 the following features are missing
62 a. control of exception and rounding modes
63 b. scalb (y, N)
64 c. logb (x)
65 d. nextafter(x,y)
66 e. next towards
67 3. division by zero currently (python 2.5) raises excaption and the
68 resulting inf/nan cannot be propogated
69 4. a second module ieeefloatcapabilities (currently incomplete)
70 provides tests of the capabilites of a floating point implementation
71 on a specific python platform
72 5. development and conventions on byte order come from a little endian
73 (intel) platform
74 6. to reduce overheads all functions that take python float arguments do
75 _no type_ conversion thus if other numeric types are passed the functions
76 will raise exceptions, (I am not sure this is the best behaviour however,
77 as python functions should be polymorphic...)
78 7. in most cases conversion to c code for performance reasons would be trivial
79
80 ieee-754 double format:
81 63 sign bit
82 62-52 exponent (offset by 1023 value - field-1023
83 51-0 mantissa each bit n counts as 1/2^n, running from 1/2 which is the
84 most significant bit to 1/2^51, The 1/0 bit is defined by the
85 exponent field if it has any bits set if it has bits set then
86 precede the mantissa with a 1 (normalised otherwise procede it by
87 a 0 (denormalised)
88
89
90 todo:
91 unit test suite
92 test under windows
93 test under a solaris sparc box (big endian)
94 add example ieee double
95 check byte/nibble atributions
96 '''
97 from struct import pack,unpack
98 import sys
99
100
101 SIGNBIT = 0x80
102 ''' bit pattern for the sign bit in byte 8 0b00000001 of a ieee-754 double'''
103
104
105 EXPONENT_ALL_ONES_BYTE_1 = 0x7F
106 ''' value of the first byte (byte 8) in the mantisaa of a ieee-754 double that
107 is all ones (0b11111110) '''
108
109 EXPONENT_ALL_ONES_BYTE_0 = 0xF << 4
110 ''' value of the second byte (byte 7) in the mantisaa of a ieee-754 double that
111 is all ones (0b00001111) '''
112
113
114 MANTISSA_NIBBLE_MASK=0x0F
115 ''' mask to select the bits from the first nibble of byte 7 of an ieee-754
116 which is part of the mantissa (0b00001111)'''
117
118 EXPONENT_NIBBLE_MASK=0xF0
119 ''' mask to select the bits from the second nibble of byte 7 of an ieee-754
120 which is part of the exponent (0b11110000)'''
121
122
123 EXPONENT_SIGN_MASK= 0x7F
124 '''' mask to select only bits from byte 8 of an ieee-754 double that are
125 not part of the sign bit (0b11111110)'''
126
127 ''' classes of floating point numbers'''
128 CLASS_POS_INF = 1
129 CLASS_NEG_INF = 2
130 CLASS_POS_NORMAL = 4
131 CLASS_NEG_NORMAL = 8
132 CLASS_POS_DENORMAL = 16
133 CLASS_NEG_DENORMAL = 32
134 CLASS_QUIET_NAN = 64
135 CLASS_SIGNAL_NAN = 128
136 CLASS_POS_ZERO = 256
137 CLASS_NEG_ZERO = 512
138
141
182
183
185 ''' pack 8 bytes into a python float
186
187 the function is endian aware and the data should be input in little
188 endian format. Thus byte 8 contains the most significant bit of the
189 exponent and the sign bit
190
191 bytes -- 8 bytes to pack into a python (ieee 754 double) float
192
193 returns -- a python float
194
195 throws -- an Exception if bytes contains < 8 bytes
196 type of exception not determined
197 '''
198
199 doubleString=pack('8B',*bytes)
200
201
202 if sys.byteorder == 'big':
203 doubleString = doubleString[::-1]
204
205
206 return unpack('d',doubleString)[0]
207
208
209 NAN_BYTES = [0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x7F]
210 ''' bytes for an arbitary ieee-754 not a number (nan) in little endian format
211 0b00000000 00000000 00000000 00000000 00000000 00000000 00011111 11111110 '''
212
213
214 INF_BYTES = [0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x7F]
215 ''' bytes for ieee-754 positive infinity (inf) in little endian format
216 0b00000000 00000000 00000000 00000000 00000000 00000000 00001111 11111110 '''
217
218
219 nan = packBytesAsPyFloat(NAN_BYTES)
220 ''' one of a number of possible bit patterns used to represent an ieee-754 double
221 as a python float. Do not use this value for comparisons of the form x==nan as it
222 will fail on some platforms use function isNaN instead'''
223
224
225 pos_inf = packBytesAsPyFloat(INF_BYTES)
226 ''' the value of a positive ieee-754 double infinity as a python float '''
227
228
229 neg_inf = -1 * pos_inf
230 ''' the value of a negative ieee-754 double infinity as a python float'''
231
232
234 ''' pack a python float into a binary string.
235
236 This function assumes that the python type float it represents a
237 64bit double of 8 bytes. This function reverses the resulting string if
238 the current architecture is big endian.
239
240 obj -- a python float to pack
241
242 returns -- a string of 8 bytes
243
244 throws -- throws a TypeError if the the input object isn't a python float
245
246 '''
247 if not isinstance(obj,float):
248 raise TypeError('the object recieved wasn\'t a float, type was: %s' % type(obj))
249
250
251 packed =pack('d',obj)
252
253
254 if sys.byteorder == 'big':
255 packed = packed[::-1]
256
257 return packed
258
260 ''' unpack a python float as a list of 8 bytes
261
262 This function assumes that the python type float it represents a
263 64bit double of 8 bytes
264
265 obj -- a python float to unpack
266
267 returns -- a list of 8 bytes
268
269 throws -- throws an exception if obj is not composed of 8 bytes
270
271 '''
272
273 binaryString = floatToBinaryString(obj)
274
275
276 bytes = unpack('8B',binaryString)
277
278
279 return list(bytes)
280
281
282
284 ''' get the sign bit from a python float
285
286 obj -- a python float object
287
288 returns -- the floats sign bit, this has the value 1 if the float is
289 negative otherwise 0 (positive)
290
291 throws -- throws a TypeError if the the input object isn't a python float
292
293 '''
294
295
296 unpacked = floatAsByteArray(obj)
297
298
299 return unpacked[7] & SIGNBIT
300
302 ''' test if a a pyhton float is positive
303
304 obj -- a python float object
305
306 returns -- True if the float is positive otherwise False
307
308 throws -- throws a TypeError if the the input object isn't a python float
309
310 '''
311
312 if getSignBit(obj):
313 return False
314 else:
315 return True
316
318 ''' test if a a pyhton float 64 bit ieee-74 double is negative
319
320 obj -- a python float object
321
322 returns -- True if the float is negative
323
324 throws -- tthrows a TypeError if the the input object isn't a python float
325 '''
326 return not isPositive(obj)
327
328
329
331 ''' test to see if two python float are unordered
332
333 float comparison is unordered if either or both of the objects is 'not a
334 number' (nan)
335
336 obj1 -- a python float object
337 obj2 -- a python float object
338
339 throws -- throws a TypeError if the the input objects aren't a python floats
340
341 '''
342
343
344 nanTest1 = isNaN(obj1)
345 nanTest2 = isNaN(obj2)
346
347
348 if nanTest1 or nanTest2:
349 return True
350 else:
351 return False
352
353
355 ''' test to see if a python float is finite
356
357 to be finite a number mustn't be a nan or + or - inf a result of True
358 guarantees that the number is bounded by +- inf -inf < x < inf
359
360 obj -- a python float object
361
362 throws -- throws a TypeError if the the input object isn't a python float
363
364 '''
365
366 result = True
367 if isNaN(obj):
368 result = False
369 if isInf(obj):
370 result = False
371
372
373 return result
374
376 ''' copy the sign bit from one python float to another
377
378 this function is class agnostic the sign bit can be copied freely between
379 ordinarys floats nans and +/-inf
380
381 fromDouble -- the python float to copy the sign bit from
382 toDouble -- the python float to copy the sign bit to
383
384 throws -- throws a TypeError if toDouble isn't a python float or if
385 fromNumber can't be converted to a float
386
387 '''
388
389
390 fromNumber = float(fromNumber)
391
392
393 fromIsPositive = isPositive(fromNumber)
394 toIsPositive = isPositive(toDouble)
395
396
397 toBytes = floatAsByteArray(toDouble)
398
399 if not toIsPositive and fromIsPositive:
400
401 toBytes[7] &= EXPONENT_SIGN_MASK
402
403 elif toIsPositive and not fromIsPositive:
404
405 toBytes[7] = toBytes[7] + 0x80
406
407
408 return packBytesAsPyFloat(toBytes)
409
410
412 ''' check to see if a python float is denormalised
413
414 denormalisation indicates that the number has no exponent set and all the
415 precision is in the mantissa, this is an indication that the number is
416 tending to towards underflow
417
418 obj -- python float object to check
419
420 result -- True if the number is denormalised
421
422 throws -- throws a TypeError if toDouble isn't a python float or if
423 obj isn't a float
424 '''
425
426 result = True
427
428
429 if not isExpAllZeros(obj):
430 result = False
431
432
433
434
435 if isZero(obj):
436 result = False
437
438 return result
439
440
441
442
443
444
446 ''' get the 7 bytes that makeup the mantissa of float
447
448 the mantissa is returned as the 7 bytes in the mantissa in little endian order
449 in the 7th byte the 2nd nibble of the byte is masked off as it contains
450 part of the exponent. The second nibble of the 7th byte is therefore always
451 has the value 0x0
452
453 obj -- float object to extract the mantissa from
454
455 returns -- a list of 7 bytes in little endian order
456
457 throws -- throws a TypeError if obj isn't a python float
458
459 '''
460
461
462 bytes = floatAsByteArray(obj)
463
464
465 bytes[6] = bytes[6] & MANTISSA_NIBBLE_MASK
466
467
468 return bytes[:7]
469
471 ''' get the 2 bytes that makeup the exponent of a float
472
473 the exponent is returned as the 2 bytes in the exponent in little endian order
474 in the 2nd byte the last bit is masked off as this is the sign bit. Ttherefore
475 all values have the last bit set to zero. In the first byte the first nibble of
476 the byte is also masked off as it contains part of the mantissa and thus
477 always has the value 0x0.
478
479 obj -- float object to extract the exponent from
480
481 returns -- a list of 2 bytes in little endian order
482
483 throws -- throws a TypeError if obj isn't a python float
484 '''
485
486
487 bytes = floatAsByteArray(obj)
488
489
490 bytes[6] = bytes[6] & EXPONENT_NIBBLE_MASK
491
492
493 bytes[7] = bytes[7] & EXPONENT_SIGN_MASK
494
495
496 return bytes[6:]
497
498
499
500
502 ''' check if the bits of the exponent of a float is zero
503
504 obj -- float object to check exponent for zero value
505
506 returns -- True if the exponent is zero
507
508 throws -- throws a TypeError if obj isn't a python float
509 '''
510 result = True
511
512
513 expBytes = getExponentBytes(obj)
514
515
516 if expBytes[0] > 0 or expBytes[1] > 0:
517 result = False
518
519 return result
520
522 ''' check if the bits of the mantissa of a float is zero
523
524 obj -- float object to check mantissa for zero value
525
526 returns -- True if the mantissa is zero
527
528 throws -- throws a TypeError if obj isn't a python float
529 '''
530 result = True
531
532
533 mantissaBytes = getMantissaBytes(obj)
534
535
536 for byte in mantissaBytes:
537 if byte != 0:
538 result = False
539 break
540
541 return result
542
544 ''' check if the bits of the exponent of a floatis all 1 bits
545
546 obj -- float object to check exponent for 1 bits
547
548 returns -- True if all the bits in the exponent are one
549
550 throws -- throws a TypeError if obj isn't a python float
551 '''
552
553 result = False
554
555
556 expBytes = getExponentBytes(obj)
557
558
559 if expBytes[0] == EXPONENT_ALL_ONES_BYTE_0 and expBytes[1] == EXPONENT_ALL_ONES_BYTE_1:
560 result = True
561
562 return result
563
565 ''' check to see if a python float is an ieee-754 double not a number (nan)
566
567 obj -- float object to check for not a number
568
569 returns -- True if object is not a number
570
571 throws -- throws a TypeError if obj isn't a python float
572 '''
573
574
575 result = None
576
577
578
579 if not isExpAllOnes(obj):
580 result = False
581 else:
582
583 manBytes = getMantissaBytes(obj)
584
585
586
587 for byte in manBytes:
588 if byte > 0:
589 result = True
590 break
591
592
593
594
595 return result
596
598 ''' check to see if a python float is an infinity
599
600 the check returns true for either positive or negative infinities
601
602 obj -- float object to check for infinity
603
604 returns -- True if object is an infinity
605
606 throws -- throws a TypeError if obj isn't a python float
607 '''
608
609 result = None
610
611
612
613 if not isExpAllOnes(obj):
614 result = False
615 else:
616
617 manBytes = getMantissaBytes(obj)
618
619 for byte in manBytes:
620
621
622 if byte > 0:
623 result = False
624 break
625 result = True
626
627 return result
628
630 ''' check to see if a python float is positive infinity
631
632 obj -- float object to check for positive infinity
633
634 returns -- True if object is a positive infinity
635
636 throws -- throws a TypeError if obj isn't a python float
637 '''
638 return isInf(obj) and isPositive(obj)
639
641 ''' check to see if a python float is negative infinity
642
643
644 obj -- float object to check for negative infinity
645
646 returns -- True if object is a negative infinity
647
648 throws -- throws a TypeError if obj isn't a python float
649 '''
650
651 return isInf(obj) and not isPositive(obj)
652