1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 """ieeefloat a set of functions for dealing with IEEE-754 float objects.
27
28 On most platforms Python uses IEEE-754 double objects of length 64 bits to represent floats (some
29 architectures such as older Crays and Vaxes don't). Thus an IEEE-754 double is the implementation
30 of a python float object on most platforms.
31
32 IEEE-74 uses special bit patterns to represent the following states or classes of IEEE floating
33 point numbers (IEEE-class):
34 - +/- NaN: Not a number (e.g. 0.0/0.0).
35 - inf: Positive or negative infinity (1.0/0.0).
36 - +/- zero: Zero maybe positive or negative under IEEE-754.
37
38 This module provides functions for working with python floats and their special values, if they
39 contain IEEE-754 formatted values. Specifically:
40 - Pack and unpack a list of bytes representing an IEEE-754 double to a python float (takes care
41 of little endian/big endian issues).
42 - Get the sign bit of a python float.
43 - Check the ordering of python floats allowing for NaNs (NaNs cannot normally 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, neg-zero, ...).
48 - Check that the current python float implementations uses IEEE-754 doubles.
49
50 It also provides constants containg specific bit patterns for NaN and +-inf as these values cannot
51 be generated from strings via the constructor float(x) with some compiler implementations (typically
52 older Microsoft Windows compilers).
53
54 As a convenience the names of functions and constants conform to those defined in the draft python
55 PEP 754 'IEEE 754 Floating Point Special Values'.
56
57 Notes:
58 1. Binary data is documented as binary strings e.g. 0xF0 = 0b11110000.
59 2. The module doesn't support all the functions recommended by IEEE-754, the following features
60 are missing:
61 - Control of exception and rounding modes.
62 - scalb(y, N).
63 - logb(x).
64 - nextafter(x,y).
65 - Next towards.
66 3. Division by zero currently (python 2.5) raises exception and the resulting inf/NaN cannot be
67 propogated.
68 4. A second module ieeefloatcapabilities (currently incomplete) provides tests of the
69 capabilities of a floating point implementation on a specific python platform.
70 5. Development and conventions on byte order come from a little endian (Intel) platform.
71 6. To reduce overheads all functions that take python float arguments do _no type_ conversion
72 thus if other numeric types are passed the functions will raise exceptions, (I am not sure
73 this is the best behaviour however, as python functions should be polymorphic...).
74 7. In most cases conversion to C code for performance reasons would be trivial.
75
76 IEEE-754 double format:
77 - 63 sign bit.
78 - 62-52 exponent (offset by 1023 value - field-1023).
79 - 51-0 mantissa each bit n counts as 1/2^n, running from 1/2 which is the most significant bit
80 to 1/2^51, The 1/0 bit is defined by the exponent field if it has any bits set if it has bits
81 set then precede the mantissa with a 1 (normalised otherwise precede it by a 0 (denormalised).
82
83
84 Todo:
85 - Unit test suite.
86 - Test under Windows.
87 - Test under a Solaris Sparc box (big endian).
88 - Add example IEEE double.
89 - Check byte/nibble attributions.
90 """
91 from struct import pack, unpack
92 import sys
93
94
95 SIGNBIT = 0x80
96 """Bit pattern for the sign bit in byte 8 0b00000001 of a IEEE-754 double."""
97
98
99 EXPONENT_ALL_ONES_BYTE_1 = 0x7F
100 """Value of the first byte (byte 8) in the mantissa of a IEEE-754 double that is all ones
101 (0b11111110)."""
102
103 EXPONENT_ALL_ONES_BYTE_0 = 0xF << 4
104 """Value of the second byte (byte 7) in the mantissa of a IEEE-754 double that is all ones
105 (0b00001111)."""
106
107
108 MANTISSA_NIBBLE_MASK=0x0F
109 """Mask to select the bits from the first nibble of byte 7 of an IEEE-754 which is part of the
110 mantissa (0b00001111)."""
111
112 EXPONENT_NIBBLE_MASK=0xF0
113 """Mask to select the bits from the second nibble of byte 7 of an IEEE-754 which is part of the
114 exponent (0b11110000)."""
115
116
117 EXPONENT_SIGN_MASK= 0x7F
118 """Mask to select only bits from byte 8 of an IEEE-754 double that are not part of the sign bit
119 (0b11111110)."""
120
121 """Classes of floating point numbers."""
122 CLASS_POS_INF = 1
123 CLASS_NEG_INF = 2
124 CLASS_POS_NORMAL = 4
125 CLASS_NEG_NORMAL = 8
126 CLASS_POS_DENORMAL = 16
127 CLASS_NEG_DENORMAL = 32
128 CLASS_QUIET_NAN = 64
129 CLASS_SIGNAL_NAN = 128
130 CLASS_POS_ZERO = 256
131 CLASS_NEG_ZERO = 512
132
133
136
137
179
180
182 """Pack 8 bytes into a python float.
183
184 The function is endian aware and the data should be input in little endian format. Thus byte 8
185 contains the most significant bit of the exponent and the sign bit.
186
187 @param bytes: 8 bytes to pack into a python (IEEE 754 double) float.
188 @type bytes: float
189 @return: A python float
190 @rtype: float
191 @raise TypeError: If bytes contains < 8 bytes type of exception not determined.
192 """
193
194
195 doubleString=pack('8B',*bytes)
196
197
198 if sys.byteorder == 'big':
199 doubleString = doubleString[::-1]
200
201
202 return unpack('d', doubleString)[0]
203
204
205 NAN_BYTES = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x7F]
206 """Bytes for an arbitary IEEE-754 not a number (NaN) in little endian format
207 0b00000000 00000000 00000000 00000000 00000000 00000000 00011111 11111110."""
208
209
210 INF_BYTES = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F]
211 """Bytes for IEEE-754 positive infinity (inf) in little endian format
212 0b00000000 00000000 00000000 00000000 00000000 00000000 00001111 11111110."""
213
214
215 nan = packBytesAsPyFloat(NAN_BYTES)
216 """One of a number of possible bit patterns used to represent an IEEE-754 double as a python float.
217 Do not use this value for comparisons of the form x==NaN as it will fail on some platforms use
218 function isNaN instead."""
219
220
221 pos_inf = packBytesAsPyFloat(INF_BYTES)
222 """The value of a positive IEEE-754 double infinity as a python float."""
223
224
225 neg_inf = -1 * pos_inf
226 """The value of a negative IEEE-754 double infinity as a python float."""
227
228
230 """Pack a python float into a binary string.
231
232 This function assumes that the python type float it represents a 64bit double of 8 bytes. This
233 function reverses the resulting string if the current architecture is big endian.
234
235 @param obj: A python float to pack.
236 @type obj: float
237 @return: A string of 8 bytes.
238 @rtype: str
239 @raise TypeError: If the input object isn't a python float.
240 """
241
242 if not isinstance(obj, float):
243 raise TypeError('the object recieved wasn\'t a float, type was: %s' % type(obj))
244
245
246 packed =pack('d', obj)
247
248
249 if sys.byteorder == 'big':
250 packed = packed[::-1]
251
252 return packed
253
254
256 """Unpack a python float as a list of 8 bytes.
257
258 This function assumes that the python type float it represents a 64bit double of 8 bytes.
259
260 @param obj: A python float to unpack.
261 @type obj: float
262 @return: A list of 7 bytes.
263 @rtype: list of str
264 @raise TypeError: If obj is not composed of 8 bytes.
265 """
266
267
268 binaryString = floatToBinaryString(obj)
269
270
271 bytes = unpack('8B', binaryString)
272
273
274 return list(bytes)
275
276
278 """Get the sign bit from a python float.
279
280 @param obj: A python float object.
281 @type obj: float
282 @return: The float's sign bit, this has the value 1 if the float is negative
283 otherwise 0 (positive).
284 @rtype: bit
285 @raise TypeError: If the input object isn't a python float.
286 """
287
288
289 unpacked = floatAsByteArray(obj)
290
291
292 return unpacked[7] & SIGNBIT
293
294
296 """Test if a python float is positive.
297
298 @param obj: A python float object.
299 @type obj: float
300 @return: True if the float is positive otherwise False.
301 @rtype: bool
302 @raise TypeError: If the input object isn't a python float.
303 """
304
305 if getSignBit(obj):
306 return False
307 else:
308 return True
309
310
312 """Test if a python float 64 bit IEEE-74 double is negative.
313
314 @param obj: A python float object.
315 @type obj: float
316 @return: True if the float is negative.
317 @rtype: bool
318 @raise TypeError: If the input object isn't a python float.
319 """
320
321 return not isPositive(obj)
322
323
325 """Test to see if two python float are unordered.
326
327 Float comparison is unordered if either or both of the objects is 'not a number' (NaN).
328
329 @param obj1: A python float object
330 @type obj1: float
331 @param obj2: A python float object
332 @type obj2: float
333 @return: True if one of the args is a NaN.
334 @rtype: bool
335 @raise TypeError: If the input objects aren't python floats.
336 """
337
338
339 nanTest1 = isNaN(obj1)
340 nanTest2 = isNaN(obj2)
341
342
343 if nanTest1 or nanTest2:
344 return True
345 else:
346 return False
347
348
350 """Test to see if a python float is finite.
351
352 To be finite a number mustn't be a NaN or +/- inf. A result of True guarantees that the number
353 is bounded by +/- inf, -inf < x < inf.
354
355 @param obj: A python float object.
356 @type obj: float
357 @return: True if the float is finite.
358 @rtype: bool
359 @raise TypeError: If the input object isn't a python float.
360 """
361
362 result = True
363 if isNaN(obj):
364 result = False
365 if isInf(obj):
366 result = False
367
368
369 return result
370
371
373 """Copy the sign bit from one python float to another.
374
375 This function is class agnostic the sign bit can be copied freely between ordinary floats, NaNs
376 and +/- inf.
377
378 @param fromNumber: The python float to copy the sign bit from.
379 @type fromNumber: float
380 @param toDouble: The python float to copy the sign bit to.
381 @type toDouble: float
382 @raise TypeError: If toDouble isn't a python float or if fromNumber can't be converted to a
383 float.
384 """
385
386
387 fromNumber = float(fromNumber)
388
389
390 fromIsPositive = isPositive(fromNumber)
391 toIsPositive = isPositive(toDouble)
392
393
394 toBytes = floatAsByteArray(toDouble)
395
396 if not toIsPositive and fromIsPositive:
397
398 toBytes[7] &= EXPONENT_SIGN_MASK
399
400 elif toIsPositive and not fromIsPositive:
401
402 toBytes[7] = toBytes[7] + 0x80
403
404
405 return packBytesAsPyFloat(toBytes)
406
407
409 """Check to see if a python float is denormalised.
410
411 Denormalisation indicates that the number has no exponent set and all the precision is in the
412 mantissa, this is an indication that the number is tending to towards underflow.
413
414 @param obj: Python float object to check.
415 @type obj: float
416 @return: True if the number is denormalised.
417 @rtype: bool
418 @raise TypeError: If toDouble isn't a python float or if obj isn't a float.
419 """
420
421 result = True
422
423
424 if not isExpAllZeros(obj):
425 result = False
426
427
428
429
430 if isZero(obj):
431 result = False
432
433 return result
434
435
437 """Get the 7 bytes that makeup the mantissa of float.
438
439 The mantissa is returned as the 7 bytes in the mantissa in little endian order in the 7th byte
440 the 2nd nibble of the byte is masked off as it contains part of the exponent. The second nibble
441 of the 7th byte is therefore always has the value 0x0.
442
443 @param obj: Float object to extract the mantissa from.
444 @type obj: float
445 @return: A list of 7 bytes in little endian order.
446 @rtype: list of 7 bytes
447 @raise TypeError: If obj isn't a python float.
448 """
449
450
451 bytes = floatAsByteArray(obj)
452
453
454 bytes[6] = bytes[6] & MANTISSA_NIBBLE_MASK
455
456
457 return bytes[:7]
458
459
461 """Get the 2 bytes that makeup the exponent of a float.
462
463 The exponent is returned as the 2 bytes in the exponent in little endian order in the 2nd byte
464 the last bit is masked off as this is the sign bit. Therefore all values have the last bit set
465 to zero. In the first byte the first nibble of the byte is also masked off as it contains part
466 of the mantissa and thus always has the value 0x0.
467
468 @param obj: Float object to extract the exponent from.
469 @type obj: float
470 @return: A list of 2 bytes in little endian order.
471 @rtype: list of 2 bytes
472 @raise TypeError: If obj isn't a python float.
473 """
474
475
476 bytes = floatAsByteArray(obj)
477
478
479 bytes[6] = bytes[6] & EXPONENT_NIBBLE_MASK
480
481
482 bytes[7] = bytes[7] & EXPONENT_SIGN_MASK
483
484
485 return bytes[6:]
486
487
489 """Check if the bits of the exponent of a float is zero.
490
491 @param obj: Float object to check exponent for zero value.
492 @type obj: float
493 @return: True if the exponent is zero.
494 @rtype: bool
495 @raise TypeError: If obj isn't a python float.
496 """
497
498 result = True
499
500
501 expBytes = getExponentBytes(obj)
502
503
504 if expBytes[0] > 0 or expBytes[1] > 0:
505 result = False
506
507 return result
508
509
511 """Check if the bits of the mantissa of a float is zero.
512
513 @param obj: Float object to check mantissa for zero value.
514 @type obj: float
515 @return: True if the mantissa is zero.
516 @rtype: bool
517 @raise TypeError: If obj isn't a python float.
518 """
519
520 result = True
521
522
523 mantissaBytes = getMantissaBytes(obj)
524
525
526 for byte in mantissaBytes:
527 if byte != 0:
528 result = False
529 break
530
531 return result
532
533
535 """Check if the bits of the exponent of a float is all 1 bits.
536
537 @param obj: Float object to check exponent for 1 bits.
538 @type obj: float
539 @return: True if all the bits in the exponent are one.
540 @rtype: bool
541 @raise TypeError: If obj isn't a python float.
542 """
543
544 result = False
545
546
547 expBytes = getExponentBytes(obj)
548
549
550 if expBytes[0] == EXPONENT_ALL_ONES_BYTE_0 and expBytes[1] == EXPONENT_ALL_ONES_BYTE_1:
551 result = True
552
553 return result
554
555
557 """Check to see if a python float is an IEEE-754 double not a number (NaN).
558
559 @param obj: Float object to check for not a number.
560 @type obj: float
561 @return: True if object is not a number.
562 @rtype: bool
563 @raise TypeError: If obj isn't a python float.
564 """
565
566
567 result = None
568
569
570
571 if not isExpAllOnes(obj):
572 result = False
573 else:
574
575 manBytes = getMantissaBytes(obj)
576
577
578
579 for byte in manBytes:
580 if byte > 0:
581 result = True
582 break
583
584
585
586
587 return result
588
589
591 """Check to see if a python float is an infinity.
592
593 The check returns true for either positive or negative infinities.
594
595 @param obj: Float object to check for infinity.
596 @type obj: float
597 @return: True if object is an infinity.
598 @rtype: bool
599 @raise TypeError: If obj isn't a python float.
600 """
601
602
603 result = None
604
605
606
607 if not isExpAllOnes(obj):
608 result = False
609 else:
610
611 manBytes = getMantissaBytes(obj)
612
613 for byte in manBytes:
614
615
616 if byte > 0:
617 result = False
618 break
619 result = True
620
621 return result
622
623
625 """Check to see if a python float is positive infinity.
626
627 @param obj: Float object to check for positive infinity.
628 @type obj: float
629 @return: True if object is a positive infinity.
630 @rtype: bool
631 @raise TypeError: If obj isn't a python float.
632 """
633
634 return isInf(obj) and isPositive(obj)
635
636
638 """Check to see if a python float is negative infinity.
639
640 @param obj: Float object to check for negative infinity.
641 @type obj: float
642 @return: True if object is a negative infinity.
643 @rtype: bool
644 @raise TypeError: If obj isn't a python float.
645 """
646
647 return isInf(obj) and not isPositive(obj)
648
649
651 """Convert a 64 bit IEEE-754 ascii bit pattern into a 64 bit Python float.
652
653 @param string: The ascii bit pattern repesenting the IEEE-754 float.
654 @type string: str
655 @param endian: The endianness of the bit pattern (can be 'big' or 'little').
656 @type endian: str
657 @return: The 64 bit float corresponding to the IEEE-754 bit pattern.
658 @returntype: float
659 @raise TypeError: If 'string' is not a string, the length of the 'string' is not 64, or if
660 'string' does not consist solely of the characters '0' and '1'.
661 """
662
663
664 if not isinstance(string, str):
665 raise TypeError("The string argument '%s' is not a string." % string)
666
667
668 if len(string) != 64:
669 raise TypeError("The string '%s' is not 64 characters long." % string)
670
671
672 for char in string:
673 if char not in ['0', '1']:
674 raise TypeError("The string '%s' should be composed solely of the characters '0' and '1'." % string)
675
676
677 if endian == 'big' and sys.byteorder == 'little':
678 string = string[::-1]
679 elif endian == 'little' and sys.byteorder == 'big':
680 string = string[::-1]
681
682
683 bytes = []
684 for i in xrange(8):
685 bytes.append(bitpatternToInt(string[i*8:i*8+8], endian=sys.byteorder))
686
687
688 return packBytesAsPyFloat(bytes)
689
690
692 """Convert a bit pattern into its integer representation.
693
694 @param string: The ascii string repesenting binary data.
695 @type string: str
696 @param endian: The endianness of the bit pattern (can be 'big' or 'little').
697 @type endian: str
698 @return: The integer value.
699 @returntype: int
700 """
701
702
703 if not isinstance(string, str):
704 raise TypeError("The string argument '%s' is not a string." % string)
705
706
707 for char in string:
708 if char not in ['0', '1']:
709 raise TypeError("The string '%s' should be composed solely of the characters '0' and '1'." % string)
710
711
712 if endian == 'big' and sys.byteorder == 'little':
713 string = string[::-1]
714 elif endian == 'little' and sys.byteorder == 'big':
715 string = string[::-1]
716
717
718 int_val = 0
719 for i in xrange(len(string)):
720 if int(string[i]):
721 int_val = int_val + 2**i
722
723
724 return int_val
725
726
727
728
729
730
731
732 PosZero = bitpatternToFloat('0000000000000000000000000000000000000000000000000000000000000000', endian='big')
733 NegZero = bitpatternToFloat('1000000000000000000000000000000000000000000000000000000000000000', endian='big')
734 PosEpsilonDenorm = bitpatternToFloat('0000000000000000000000000000000000000000000000000000000000000001', endian='big')
735 NegEpsilonDenorm = bitpatternToFloat('1000000000000000000000000000000000000000000000000000000000000001', endian='big')
736 PosEpsilonNorm = bitpatternToFloat('0000000000010000000000000000000000000000000000000000000000000001', endian='big')
737 NegEpsilonNorm = bitpatternToFloat('1000000000010000000000000000000000000000000000000000000000000001', endian='big')
738 PosMax = bitpatternToFloat('0111111111101111111111111111111111111111111111111111111111111111', endian='big')
739 NegMin = bitpatternToFloat('1111111111101111111111111111111111111111111111111111111111111111', endian='big')
740 PosInf = bitpatternToFloat('0111111111110000000000000000000000000000000000000000000000000000', endian='big')
741 NegInf = bitpatternToFloat('1111111111110000000000000000000000000000000000000000000000000000', endian='big')
742 PosNaN_A = bitpatternToFloat('0111111111110000000000000000000000000000001000000000000000000000', endian='big')
743 NegNaN_A = bitpatternToFloat('1111111111110000000000000000000000000000001000000000000000000000', endian='big')
744 PosNaN_B = bitpatternToFloat('0111111111110000000000000000011111111111111111111110000000000000', endian='big')
745 NegNaN_B = bitpatternToFloat('1111111111110000000000000000011111111111111111111110000000000000', endian='big')
746 PosNaN_C = bitpatternToFloat('0111111111110101010101010101010101010101010101010101010101010101', endian='big')
747 NegNaN_C = bitpatternToFloat('1111111111110101010101010101010101010101010101010101010101010101', endian='big')
748 PosNaN = PosNaN_C
749 NegNaN = NegNaN_C
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767