Merge pull request #136 from Rav3nPL/patch-1
[p2pool.git] / fpconst.py
1 """Utilities for handling IEEE 754 floating point special values
2
3 This python module implements constants and functions for working with
4 IEEE754 double-precision special values.  It provides constants for
5 Not-a-Number (NaN), Positive Infinity (PosInf), and Negative Infinity
6 (NegInf), as well as functions to test for these values.
7
8 The code is implemented in pure python by taking advantage of the
9 'struct' standard module. Care has been taken to generate proper
10 results on both big-endian and little-endian machines. Some efficiency
11 could be gained by translating the core routines into C.
12
13 See <http://babbage.cs.qc.edu/courses/cs341/IEEE-754references.html>
14 for reference material on the IEEE 754 floating point standard.
15
16 Further information on this package is available at
17 <http://www.analytics.washington.edu/statcomp/projects/rzope/fpconst/>.
18
19 ------------------------------------------------------------------
20 Author:    Gregory R. Warnes <Gregory.R.Warnes@Pfizer.com>
21 Date:      2005-02-24
22 Version:   0.7.2
23 Copyright: (c) 2003-2005 Pfizer, Licensed to PSF under a Contributor Agreement
24 License:   Licensed under the Apache License, Version 2.0 (the"License");
25            you may not use this file except in compliance with the License.
26            You may obtain a copy of the License at
27
28                http://www.apache.org/licenses/LICENSE-2.0
29
30            Unless required by applicable law or agreed to in
31            writing, software distributed under the License is
32            distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
33            CONDITIONS OF ANY KIND, either express or implied.  See
34            the License for the specific language governing
35            permissions and limitations under the License.
36 ------------------------------------------------------------------
37 """
38
39 __version__ = "0.7.2"
40 ident = "$Id: fpconst.py,v 1.16 2005/02/24 17:42:03 warnes Exp $"
41
42 import struct, operator
43
44 # check endianess
45 _big_endian = struct.pack('i',1)[0] != '\x01'
46
47 # and define appropriate constants
48 if(_big_endian): 
49     NaN    = struct.unpack('d', '\x7F\xF8\x00\x00\x00\x00\x00\x00')[0]
50     PosInf = struct.unpack('d', '\x7F\xF0\x00\x00\x00\x00\x00\x00')[0]
51     NegInf = -PosInf
52 else:
53     NaN    = struct.unpack('d', '\x00\x00\x00\x00\x00\x00\xf8\xff')[0]
54     PosInf = struct.unpack('d', '\x00\x00\x00\x00\x00\x00\xf0\x7f')[0]
55     NegInf = -PosInf
56
57 def _double_as_bytes(dval):
58     "Use struct.unpack to decode a double precision float into eight bytes"
59     tmp = list(struct.unpack('8B',struct.pack('d', dval)))
60     if not _big_endian:
61         tmp.reverse()
62     return tmp
63
64 ##
65 ## Functions to extract components of the IEEE 754 floating point format
66 ##
67
68 def _sign(dval):
69     "Extract the sign bit from a double-precision floating point value"
70     bb = _double_as_bytes(dval)
71     return bb[0] >> 7 & 0x01
72
73 def _exponent(dval):
74     """Extract the exponentent bits from a double-precision floating
75     point value.
76
77     Note that for normalized values, the exponent bits have an offset
78     of 1023. As a consequence, the actual exponentent is obtained
79     by subtracting 1023 from the value returned by this function
80     """
81     bb = _double_as_bytes(dval)
82     return (bb[0] << 4 | bb[1] >> 4) & 0x7ff
83
84 def _mantissa(dval):
85     """Extract the _mantissa bits from a double-precision floating
86     point value."""
87
88     bb = _double_as_bytes(dval)
89     mantissa =  bb[1] & 0x0f << 48
90     mantissa += bb[2] << 40
91     mantissa += bb[3] << 32
92     mantissa += bb[4]
93     return mantissa 
94
95 def _zero_mantissa(dval):
96     """Determine whether the mantissa bits of the given double are all
97     zero."""
98     bb = _double_as_bytes(dval)
99     return ((bb[1] & 0x0f) | reduce(operator.or_, bb[2:])) == 0
100
101 ##
102 ## Functions to test for IEEE 754 special values
103 ##
104
105 def isNaN(value):
106     "Determine if the argument is a IEEE 754 NaN (Not a Number) value."
107     return (_exponent(value)==0x7ff and not _zero_mantissa(value))
108
109 def isInf(value):
110     """Determine if the argument is an infinite IEEE 754 value (positive
111     or negative inifinity)"""
112     return (_exponent(value)==0x7ff and _zero_mantissa(value))
113
114 def isFinite(value):
115     """Determine if the argument is an finite IEEE 754 value (i.e., is
116     not NaN, positive or negative inifinity)"""
117     return (_exponent(value)!=0x7ff)
118
119 def isPosInf(value):
120     "Determine if the argument is a IEEE 754 positive infinity value"
121     return (_sign(value)==0 and _exponent(value)==0x7ff and \
122             _zero_mantissa(value))
123
124 def isNegInf(value):
125     "Determine if the argument is a IEEE 754 negative infinity value"
126     return (_sign(value)==1 and _exponent(value)==0x7ff and \
127             _zero_mantissa(value))
128
129 ##
130 ## Functions to test public functions.
131 ## 
132
133 def test_isNaN():
134     assert( not isNaN(PosInf) )
135     assert( not isNaN(NegInf) )
136     assert(     isNaN(NaN   ) )
137     assert( not isNaN(   1.0) )
138     assert( not isNaN(  -1.0) )
139
140 def test_isInf():
141     assert(     isInf(PosInf) )
142     assert(     isInf(NegInf) )
143     assert( not isInf(NaN   ) )
144     assert( not isInf(   1.0) )
145     assert( not isInf(  -1.0) )
146
147 def test_isFinite():
148     assert( not isFinite(PosInf) )
149     assert( not isFinite(NegInf) )
150     assert( not isFinite(NaN   ) )
151     assert(     isFinite(   1.0) )
152     assert(     isFinite(  -1.0) )
153
154 def test_isPosInf():
155     assert(     isPosInf(PosInf) )
156     assert( not isPosInf(NegInf) )
157     assert( not isPosInf(NaN   ) )
158     assert( not isPosInf(   1.0) )
159     assert( not isPosInf(  -1.0) )
160
161 def test_isNegInf():
162     assert( not isNegInf(PosInf) )
163     assert(     isNegInf(NegInf) )
164     assert( not isNegInf(NaN   ) )
165     assert( not isNegInf(   1.0) )
166     assert( not isNegInf(  -1.0) )
167
168 # overall test
169 def test():
170     test_isNaN()
171     test_isInf()
172     test_isFinite()
173     test_isPosInf()
174     test_isNegInf()
175     
176 if __name__ == "__main__":
177     test()
178