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