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
92
93 from struct import pack, unpack
94 import sys
95
96
97 from check_types import is_float
98
99
100 SIGNBIT = 0x80
101 """Bit pattern for the sign bit in byte 8 0b00000001 of a IEEE-754 double."""
102
103
104 EXPONENT_ALL_ONES_BYTE_1 = 0x7F
105 """Value of the first byte (byte 8) in the mantissa of a IEEE-754 double that is all ones
106 (0b11111110)."""
107
108 EXPONENT_ALL_ONES_BYTE_0 = 0xF << 4
109 """Value of the second byte (byte 7) in the mantissa of a IEEE-754 double that is all ones
110 (0b00001111)."""
111
112
113 MANTISSA_NIBBLE_MASK=0x0F
114 """Mask to select the bits from the first nibble of byte 7 of an IEEE-754 which is part of the
115 mantissa (0b00001111)."""
116
117 EXPONENT_NIBBLE_MASK=0xF0
118 """Mask to select the bits from the second nibble of byte 7 of an IEEE-754 which is part of the
119 exponent (0b11110000)."""
120
121
122 EXPONENT_SIGN_MASK= 0x7F
123 """Mask to select only bits from byte 8 of an IEEE-754 double that are not part of the sign bit
124 (0b11111110)."""
125
126 """Classes of floating point numbers."""
127 CLASS_POS_INF = 1
128 CLASS_NEG_INF = 2
129 CLASS_POS_NORMAL = 4
130 CLASS_NEG_NORMAL = 8
131 CLASS_POS_DENORMAL = 16
132 CLASS_NEG_DENORMAL = 32
133 CLASS_QUIET_NAN = 64
134 CLASS_SIGNAL_NAN = 128
135 CLASS_POS_ZERO = 256
136 CLASS_NEG_ZERO = 512
137
138
141
142
184
185
187 """Pack 8 bytes into a python float.
188
189 The function is endian aware and the data should be input in little endian format. Thus byte 8
190 contains the most significant bit of the exponent and the sign bit.
191
192 @param bytes: 8 bytes to pack into a python (IEEE 754 double) float.
193 @type bytes: float
194 @return: A python float
195 @rtype: float
196 @raise TypeError: If bytes contains < 8 bytes type of exception not determined.
197 """
198
199
200 doubleString=pack('8B',*bytes)
201
202
203 if sys.byteorder == 'big':
204 doubleString = doubleString[::-1]
205
206
207 return unpack('d', doubleString)[0]
208
209
210 NAN_BYTES = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x7F]
211 """Bytes for an arbitary IEEE-754 not a number (NaN) in little endian format
212 0b00000000 00000000 00000000 00000000 00000000 00000000 00011111 11111110."""
213
214
215 INF_BYTES = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F]
216 """Bytes for IEEE-754 positive infinity (inf) in little endian format
217 0b00000000 00000000 00000000 00000000 00000000 00000000 00001111 11111110."""
218
219
220 nan = packBytesAsPyFloat(NAN_BYTES)
221 """One of a number of possible bit patterns used to represent an IEEE-754 double as a python float.
222 Do not use this value for comparisons of the form x==NaN as it will fail on some platforms use
223 function isNaN instead."""
224
225
226 pos_inf = packBytesAsPyFloat(INF_BYTES)
227 """The value of a positive IEEE-754 double infinity as a python float."""
228
229
230 neg_inf = -1 * pos_inf
231 """The value of a negative IEEE-754 double infinity as a python float."""
232
233
235 """Pack a python float into a binary string.
236
237 This function assumes that the python type float it represents a 64bit double of 8 bytes. This
238 function reverses the resulting string if the current architecture is big endian.
239
240 @param obj: A python float to pack.
241 @type obj: float
242 @return: A string of 8 bytes.
243 @rtype: str
244 @raise TypeError: If the input object isn't a python float.
245 """
246
247 if not is_float(obj):
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
259
261 """Unpack a python float as a list of 8 bytes.
262
263 This function assumes that the python type float it represents a 64bit double of 8 bytes.
264
265 @param obj: A python float to unpack.
266 @type obj: float
267 @return: A list of 7 bytes.
268 @rtype: list of str
269 @raise TypeError: 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
283 """Get the sign bit from a python float.
284
285 @param obj: A python float object.
286 @type obj: float
287 @return: The float's sign bit, this has the value 1 if the float is negative
288 otherwise 0 (positive).
289 @rtype: bit
290 @raise TypeError: If the input object isn't a python float.
291 """
292
293
294 unpacked = floatAsByteArray(obj)
295
296
297 return unpacked[7] & SIGNBIT
298
299
301 """Test if a python float is positive.
302
303 @param obj: A python float object.
304 @type obj: float
305 @return: True if the float is positive otherwise False.
306 @rtype: bool
307 @raise TypeError: If the input object isn't a python float.
308 """
309
310 if getSignBit(obj):
311 return False
312 else:
313 return True
314
315
317 """Test if a python float 64 bit IEEE-74 double is negative.
318
319 @param obj: A python float object.
320 @type obj: float
321 @return: True if the float is negative.
322 @rtype: bool
323 @raise TypeError: If the input object isn't a python float.
324 """
325
326 return not isPositive(obj)
327
328
330 """Test to see if two python float are unordered.
331
332 Float comparison is unordered if either or both of the objects is 'not a number' (NaN).
333
334 @param obj1: A python float object
335 @type obj1: float
336 @param obj2: A python float object
337 @type obj2: float
338 @return: True if one of the args is a NaN.
339 @rtype: bool
340 @raise TypeError: If the input objects aren't python floats.
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 +/- inf. A result of True guarantees that the number
358 is bounded by +/- inf, -inf < x < inf.
359
360 @param obj: A python float object.
361 @type obj: float
362 @return: True if the float is finite.
363 @rtype: bool
364 @raise TypeError: If the input object isn't a python float.
365 """
366
367 result = True
368 if isNaN(obj):
369 result = False
370 if isInf(obj):
371 result = False
372
373
374 return result
375
376
378 """Copy the sign bit from one python float to another.
379
380 This function is class agnostic the sign bit can be copied freely between ordinary floats, NaNs
381 and +/- inf.
382
383 @param fromNumber: The python float to copy the sign bit from.
384 @type fromNumber: float
385 @param toDouble: The python float to copy the sign bit to.
386 @type toDouble: float
387 @raise TypeError: If toDouble isn't a python float or if fromNumber can't be converted to a
388 float.
389 """
390
391
392 fromNumber = float(fromNumber)
393
394
395 fromIsPositive = isPositive(fromNumber)
396 toIsPositive = isPositive(toDouble)
397
398
399 toBytes = floatAsByteArray(toDouble)
400
401 if not toIsPositive and fromIsPositive:
402
403 toBytes[7] &= EXPONENT_SIGN_MASK
404
405 elif toIsPositive and not fromIsPositive:
406
407 toBytes[7] = toBytes[7] + 0x80
408
409
410 return packBytesAsPyFloat(toBytes)
411
412
414 """Check to see if a python float is denormalised.
415
416 Denormalisation indicates that the number has no exponent set and all the precision is in the
417 mantissa, this is an indication that the number is tending to towards underflow.
418
419 @param obj: Python float object to check.
420 @type obj: float
421 @return: True if the number is denormalised.
422 @rtype: bool
423 @raise TypeError: If toDouble isn't a python float or if 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
442 """Get the 7 bytes that makeup the mantissa of float.
443
444 The mantissa is returned as the 7 bytes in the mantissa in little endian order in the 7th byte
445 the 2nd nibble of the byte is masked off as it contains part of the exponent. The second nibble
446 of the 7th byte is therefore always has the value 0x0.
447
448 @param obj: Float object to extract the mantissa from.
449 @type obj: float
450 @return: A list of 7 bytes in little endian order.
451 @rtype: list of 7 bytes
452 @raise TypeError: If obj isn't a python float.
453 """
454
455
456 bytes = floatAsByteArray(obj)
457
458
459 bytes[6] = bytes[6] & MANTISSA_NIBBLE_MASK
460
461
462 return bytes[:7]
463
464
466 """Get the 2 bytes that makeup the exponent of a float.
467
468 The exponent is returned as the 2 bytes in the exponent in little endian order in the 2nd byte
469 the last bit is masked off as this is the sign bit. Therefore all values have the last bit set
470 to zero. In the first byte the first nibble of the byte is also masked off as it contains part
471 of the mantissa and thus always has the value 0x0.
472
473 @param obj: Float object to extract the exponent from.
474 @type obj: float
475 @return: A list of 2 bytes in little endian order.
476 @rtype: list of 2 bytes
477 @raise TypeError: If obj isn't a python float.
478 """
479
480
481 bytes = floatAsByteArray(obj)
482
483
484 bytes[6] = bytes[6] & EXPONENT_NIBBLE_MASK
485
486
487 bytes[7] = bytes[7] & EXPONENT_SIGN_MASK
488
489
490 return bytes[6:]
491
492
494 """Check if the bits of the exponent of a float is zero.
495
496 @param obj: Float object to check exponent for zero value.
497 @type obj: float
498 @return: True if the exponent is zero.
499 @rtype: bool
500 @raise TypeError: If obj isn't a python float.
501 """
502
503 result = True
504
505
506 expBytes = getExponentBytes(obj)
507
508
509 if expBytes[0] > 0 or expBytes[1] > 0:
510 result = False
511
512 return result
513
514
516 """Check if the bits of the mantissa of a float is zero.
517
518 @param obj: Float object to check mantissa for zero value.
519 @type obj: float
520 @return: True if the mantissa is zero.
521 @rtype: bool
522 @raise TypeError: If obj isn't a python float.
523 """
524
525 result = True
526
527
528 mantissaBytes = getMantissaBytes(obj)
529
530
531 for byte in mantissaBytes:
532 if byte != 0:
533 result = False
534 break
535
536 return result
537
538
540 """Check if the bits of the exponent of a float is all 1 bits.
541
542 @param obj: Float object to check exponent for 1 bits.
543 @type obj: float
544 @return: True if all the bits in the exponent are one.
545 @rtype: bool
546 @raise TypeError: If obj isn't a python float.
547 """
548
549 result = False
550
551
552 expBytes = getExponentBytes(obj)
553
554
555 if expBytes[0] == EXPONENT_ALL_ONES_BYTE_0 and expBytes[1] == EXPONENT_ALL_ONES_BYTE_1:
556 result = True
557
558 return result
559
560
562 """Check to see if a python float is an IEEE-754 double not a number (NaN).
563
564 @param obj: Float object to check for not a number.
565 @type obj: float
566 @return: True if object is not a number.
567 @rtype: bool
568 @raise TypeError: If obj isn't a python float.
569 """
570
571
572 result = None
573
574
575
576 if not isExpAllOnes(obj):
577 result = False
578 else:
579
580 manBytes = getMantissaBytes(obj)
581
582
583
584 for byte in manBytes:
585 if byte > 0:
586 result = True
587 break
588
589
590
591
592 return result
593
594
596 """Check to see if a python float is an infinity.
597
598 The check returns true for either positive or negative infinities.
599
600 @param obj: Float object to check for infinity.
601 @type obj: float
602 @return: True if object is an infinity.
603 @rtype: bool
604 @raise TypeError: If obj isn't a python float.
605 """
606
607
608 result = None
609
610
611
612 if not isExpAllOnes(obj):
613 result = False
614 else:
615
616 manBytes = getMantissaBytes(obj)
617
618 for byte in manBytes:
619
620
621 if byte > 0:
622 result = False
623 break
624 result = True
625
626 return result
627
628
630 """Check to see if a python float is positive infinity.
631
632 @param obj: Float object to check for positive infinity.
633 @type obj: float
634 @return: True if object is a positive infinity.
635 @rtype: bool
636 @raise TypeError: If obj isn't a python float.
637 """
638
639 return isInf(obj) and isPositive(obj)
640
641
643 """Check to see if a python float is negative infinity.
644
645 @param obj: Float object to check for negative infinity.
646 @type obj: float
647 @return: True if object is a negative infinity.
648 @rtype: bool
649 @raise TypeError: If obj isn't a python float.
650 """
651
652 return isInf(obj) and not isPositive(obj)
653
654
656 """Convert a 64 bit IEEE-754 ascii bit pattern into a 64 bit Python float.
657
658 @param string: The ascii bit pattern repesenting the IEEE-754 float.
659 @type string: str
660 @param endian: The endianness of the bit pattern (can be 'big' or 'little').
661 @type endian: str
662 @return: The 64 bit float corresponding to the IEEE-754 bit pattern.
663 @returntype: float
664 @raise TypeError: If 'string' is not a string, the length of the 'string' is not 64, or if
665 'string' does not consist solely of the characters '0' and '1'.
666 """
667
668
669 if not isinstance(string, str):
670 raise TypeError("The string argument '%s' is not a string." % string)
671
672
673 if len(string) != 64:
674 raise TypeError("The string '%s' is not 64 characters long." % string)
675
676
677 for char in string:
678 if char not in ['0', '1']:
679 raise TypeError("The string '%s' should be composed solely of the characters '0' and '1'." % string)
680
681
682 if endian == 'big' and sys.byteorder == 'little':
683 string = string[::-1]
684 elif endian == 'little' and sys.byteorder == 'big':
685 string = string[::-1]
686
687
688 bytes = []
689 for i in range(8):
690 bytes.append(bitpatternToInt(string[i*8:i*8+8], endian=sys.byteorder))
691
692
693 return packBytesAsPyFloat(bytes)
694
695
697 """Convert a bit pattern into its integer representation.
698
699 @param string: The ascii string repesenting binary data.
700 @type string: str
701 @param endian: The endianness of the bit pattern (can be 'big' or 'little').
702 @type endian: str
703 @return: The integer value.
704 @returntype: int
705 """
706
707
708 if not isinstance(string, str):
709 raise TypeError("The string argument '%s' is not a string." % string)
710
711
712 for char in string:
713 if char not in ['0', '1']:
714 raise TypeError("The string '%s' should be composed solely of the characters '0' and '1'." % string)
715
716
717 if endian == 'big' and sys.byteorder == 'little':
718 string = string[::-1]
719 elif endian == 'little' and sys.byteorder == 'big':
720 string = string[::-1]
721
722
723 int_val = 0
724 for i in range(len(string)):
725 if int(string[i]):
726 int_val = int_val + 2**i
727
728
729 return int_val
730
731
732
733
734
735
736
737 PosZero = bitpatternToFloat('0000000000000000000000000000000000000000000000000000000000000000', endian='big')
738 NegZero = bitpatternToFloat('1000000000000000000000000000000000000000000000000000000000000000', endian='big')
739 PosEpsilonDenorm = bitpatternToFloat('0000000000000000000000000000000000000000000000000000000000000001', endian='big')
740 NegEpsilonDenorm = bitpatternToFloat('1000000000000000000000000000000000000000000000000000000000000001', endian='big')
741 PosEpsilonNorm = bitpatternToFloat('0000000000010000000000000000000000000000000000000000000000000001', endian='big')
742 NegEpsilonNorm = bitpatternToFloat('1000000000010000000000000000000000000000000000000000000000000001', endian='big')
743 PosMax = bitpatternToFloat('0111111111101111111111111111111111111111111111111111111111111111', endian='big')
744 NegMin = bitpatternToFloat('1111111111101111111111111111111111111111111111111111111111111111', endian='big')
745 PosInf = bitpatternToFloat('0111111111110000000000000000000000000000000000000000000000000000', endian='big')
746 NegInf = bitpatternToFloat('1111111111110000000000000000000000000000000000000000000000000000', endian='big')
747 PosNaN_A = bitpatternToFloat('0111111111110000000000000000000000000000001000000000000000000000', endian='big')
748 NegNaN_A = bitpatternToFloat('1111111111110000000000000000000000000000001000000000000000000000', endian='big')
749 PosNaN_B = bitpatternToFloat('0111111111110000000000000000011111111111111111111110000000000000', endian='big')
750 NegNaN_B = bitpatternToFloat('1111111111110000000000000000011111111111111111111110000000000000', endian='big')
751 PosNaN_C = bitpatternToFloat('0111111111110101010101010101010101010101010101010101010101010101', endian='big')
752 NegNaN_C = bitpatternToFloat('1111111111110101010101010101010101010101010101010101010101010101', endian='big')
753 PosNaN = PosNaN_C
754 NegNaN = NegNaN_C
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772