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 lib.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 if obj == None:
573 return False
574
575
576 result = None
577
578
579
580 if not isExpAllOnes(obj):
581 result = False
582 else:
583
584 manBytes = getMantissaBytes(obj)
585
586
587
588 for byte in manBytes:
589 if byte > 0:
590 result = True
591 break
592
593
594
595
596 return result
597
598
600 """Check to see if a python float is an infinity.
601
602 The check returns true for either positive or negative infinities.
603
604 @param obj: Float object to check for infinity.
605 @type obj: float
606 @return: True if object is an infinity.
607 @rtype: bool
608 @raise TypeError: If obj isn't a python float.
609 """
610
611
612 if obj == None:
613 return False
614
615
616 result = None
617
618
619
620 if not isExpAllOnes(obj):
621 result = False
622 else:
623
624 manBytes = getMantissaBytes(obj)
625
626 for byte in manBytes:
627
628
629 if byte > 0:
630 result = False
631 break
632 result = True
633
634 return result
635
636
638 """Check to see if a python float is positive infinity.
639
640 @param obj: Float object to check for positive infinity.
641 @type obj: float
642 @return: True if object is a positive infinity.
643 @rtype: bool
644 @raise TypeError: If obj isn't a python float.
645 """
646
647 return isInf(obj) and isPositive(obj)
648
649
651 """Check to see if a python float is negative infinity.
652
653 @param obj: Float object to check for negative infinity.
654 @type obj: float
655 @return: True if object is a negative infinity.
656 @rtype: bool
657 @raise TypeError: If obj isn't a python float.
658 """
659
660 return isInf(obj) and not isPositive(obj)
661
662
664 """Convert a 64 bit IEEE-754 ascii bit pattern into a 64 bit Python float.
665
666 @param string: The ascii bit pattern repesenting the IEEE-754 float.
667 @type string: str
668 @param endian: The endianness of the bit pattern (can be 'big' or 'little').
669 @type endian: str
670 @return: The 64 bit float corresponding to the IEEE-754 bit pattern.
671 @returntype: float
672 @raise TypeError: If 'string' is not a string, the length of the 'string' is not 64, or if
673 'string' does not consist solely of the characters '0' and '1'.
674 """
675
676
677 if not isinstance(string, str):
678 raise TypeError("The string argument '%s' is not a string." % string)
679
680
681 if len(string) != 64:
682 raise TypeError("The string '%s' is not 64 characters long." % string)
683
684
685 for char in string:
686 if char not in ['0', '1']:
687 raise TypeError("The string '%s' should be composed solely of the characters '0' and '1'." % string)
688
689
690 if endian == 'big' and sys.byteorder == 'little':
691 string = string[::-1]
692 elif endian == 'little' and sys.byteorder == 'big':
693 string = string[::-1]
694
695
696 bytes = []
697 for i in range(8):
698 bytes.append(bitpatternToInt(string[i*8:i*8+8], endian=sys.byteorder))
699
700
701 return packBytesAsPyFloat(bytes)
702
703
705 """Convert a bit pattern into its integer representation.
706
707 @param string: The ascii string repesenting binary data.
708 @type string: str
709 @param endian: The endianness of the bit pattern (can be 'big' or 'little').
710 @type endian: str
711 @return: The integer value.
712 @returntype: int
713 """
714
715
716 if not isinstance(string, str):
717 raise TypeError("The string argument '%s' is not a string." % string)
718
719
720 for char in string:
721 if char not in ['0', '1']:
722 raise TypeError("The string '%s' should be composed solely of the characters '0' and '1'." % string)
723
724
725 if endian == 'big' and sys.byteorder == 'little':
726 string = string[::-1]
727 elif endian == 'little' and sys.byteorder == 'big':
728 string = string[::-1]
729
730
731 int_val = 0
732 for i in range(len(string)):
733 if int(string[i]):
734 int_val = int_val + 2**i
735
736
737 return int_val
738
739
740
741
742
743
744
745 PosZero = bitpatternToFloat('0000000000000000000000000000000000000000000000000000000000000000', endian='big')
746 NegZero = bitpatternToFloat('1000000000000000000000000000000000000000000000000000000000000000', endian='big')
747 PosEpsilonDenorm = bitpatternToFloat('0000000000000000000000000000000000000000000000000000000000000001', endian='big')
748 NegEpsilonDenorm = bitpatternToFloat('1000000000000000000000000000000000000000000000000000000000000001', endian='big')
749 PosEpsilonNorm = bitpatternToFloat('0000000000010000000000000000000000000000000000000000000000000001', endian='big')
750 NegEpsilonNorm = bitpatternToFloat('1000000000010000000000000000000000000000000000000000000000000001', endian='big')
751 PosMax = bitpatternToFloat('0111111111101111111111111111111111111111111111111111111111111111', endian='big')
752 NegMin = bitpatternToFloat('1111111111101111111111111111111111111111111111111111111111111111', endian='big')
753 PosInf = bitpatternToFloat('0111111111110000000000000000000000000000000000000000000000000000', endian='big')
754 NegInf = bitpatternToFloat('1111111111110000000000000000000000000000000000000000000000000000', endian='big')
755 PosNaN_A = bitpatternToFloat('0111111111110000000000000000000000000000001000000000000000000000', endian='big')
756 NegNaN_A = bitpatternToFloat('1111111111110000000000000000000000000000001000000000000000000000', endian='big')
757 PosNaN_B = bitpatternToFloat('0111111111110000000000000000011111111111111111111110000000000000', endian='big')
758 NegNaN_B = bitpatternToFloat('1111111111110000000000000000011111111111111111111110000000000000', endian='big')
759 PosNaN_C = bitpatternToFloat('0111111111110101010101010101010101010101010101010101010101010101', endian='big')
760 NegNaN_C = bitpatternToFloat('1111111111110101010101010101010101010101010101010101010101010101', endian='big')
761 PosNaN = PosNaN_C
762 NegNaN = NegNaN_C
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780