[prev in list] [next in list] [prev in thread] [next in thread]
List: relax-commits
Subject: r2716 - /1.2/float.py
From: garyt () domain ! hid
Date: 2006-10-31 11:31:13
Message-ID: E1Gerpp-0005Gx-Kh () domain ! hid
[Download RAW message or body]
Author: varioustoxins
Date: Tue Oct 31 12:31:13 2006
New Revision: 2716
URL: http://svn.gna.org/viewcvs/relax?rev=2716&view=rev
Log:
tidied code and added comments, code is now 4 space indented
task 3621 https://gna.org/task/?3621
Modified:
1.2/float.py
Modified: 1.2/float.py
URL: http://svn.gna.org/viewcvs/relax/1.2/float.py?rev=2716&r1=2715&r2=2716&view=diff
==============================================================================
--- 1.2/float.py (original)
+++ 1.2/float.py Tue Oct 31 12:31:13 2006
@@ -30,24 +30,24 @@
ieee-74 uses special bit patterns to represent the following states or classes
of ieee floating point numbers (ieee-class)
- +-nan - not a number (e.g. 0.0/0.0)
- inf - positive or negative infinity (1.0/0.0)
- +-zero - zero maybe positive or negative under ieee-754
+ +-nan - not a number (e.g. 0.0/0.0)
+ inf - positive or negative infinity (1.0/0.0)
+ +-zero - zero maybe positive or negative under ieee-754
this module provides functions for working with python floats and their
special values, if they contain ieee-754 formatted values. Specifically
- - pack and unpack a list of bytes representing an ieee-754 double to a python
- float (takes care of little endian/big endian issues)
- - get the sign bit of a python float
- - check the ordering of python floats allowing for nans (nans cannot normally
- be compared)
- - check if a value is finite (as opposed to nan or inf)
- - copy the sign of one float to another irrespective of if it's ieee-class
- - check if a float is denormalised (and might be about to underflow)
- - check the ieee-class of a python float (nan, pos-inf, neg-inf,pos-zero,
- neg-zero,...)
- - check that the current python float implmentations uses ieee-754 doubles
-
+ - pack and unpack a list of bytes representing an ieee-754 double to a python
+ float (takes care of little endian/big endian issues)
+ - get the sign bit of a python float
+ - check the ordering of python floats allowing for nans (nans cannot normally
+ be compared)
+ - check if a value is finite (as opposed to nan or inf)
+ - copy the sign of one float to another irrespective of if it's ieee-class
+ - check if a float is denormalised (and might be about to underflow)
+ - check the ieee-class of a python float (nan, pos-inf, neg-inf,pos-zero,
+ neg-zero,...)
+ - check that the current python float implmentations uses ieee-754 doubles
+
It also provides constants containg specific bit patterns for nan and +-inf as
these values cannot be generated from strings via the constructor float(x)
with some compiler implementations (typically older microsoft windows compilers)
@@ -56,43 +56,43 @@
in the draft python PEP 754 'IEEE 754 Floating Point Special Values'
notes:
- 1. binary data is docuemented as binary strings e.g. 0xF0 = 0b11110000
- 2. the module doesn't support all the functions recommened by ieee-754,
- the following features are missing
- a. control of exception and rounding modes
- b. scalb (y, N)
- c. logb (x)
- d. nextafter(x,y)
- e. next towards
- 3. division by zero currently (python 2.5) raises excaption and the
- resulting inf/nan cannot be propogated
- 4. a second module ieeefloatcapabilities (currently incomplete)
- provides tests of the capabilites of a floating point implementation
- on a specific python platform
- 5. development and conventions on byte order come from a little endian
- (intel) platform
- 6. to reduce overheads all functions that take python float arguments do
- _no type_ conversion thus if other numeric types are passed the functions
- will raise exceptions, (I am not sure this is the best behaviour however,
- as python functions should be polymorphic...)
- 7. in most cases conversion to c code for performance reasons would be trivial
-
+ 1. binary data is docuemented as binary strings e.g. 0xF0 = 0b11110000
+ 2. the module doesn't support all the functions recommened by ieee-754,
+ the following features are missing
+ a. control of exception and rounding modes
+ b. scalb (y, N)
+ c. logb (x)
+ d. nextafter(x,y)
+ e. next towards
+ 3. division by zero currently (python 2.5) raises excaption and the
+ resulting inf/nan cannot be propogated
+ 4. a second module ieeefloatcapabilities (currently incomplete)
+ provides tests of the capabilites of a floating point implementation
+ on a specific python platform
+ 5. development and conventions on byte order come from a little endian
+ (intel) platform
+ 6. to reduce overheads all functions that take python float arguments do
+ _no type_ conversion thus if other numeric types are passed the functions
+ will raise exceptions, (I am not sure this is the best behaviour however,
+ as python functions should be polymorphic...)
+ 7. in most cases conversion to c code for performance reasons would be trivial
+
ieee-754 double format:
63 sign bit
62-52 exponent (offset by 1023 value - field-1023
51-0 mantissa each bit n counts as 1/2^n, running from 1/2 which is the
- most significant bit to 1/2^51, The 1/0 bit is defined by the
- exponent field if it has any bits set if it has bits set then
- precede the mantissa with a 1 (normalised otherwise procede it by
- a 0 (denormalised)
+ most significant bit to 1/2^51, The 1/0 bit is defined by the
+ exponent field if it has any bits set if it has bits set then
+ precede the mantissa with a 1 (normalised otherwise procede it by
+ a 0 (denormalised)
todo:
- unit test suite
- test under windows
- test under a solaris sparc box (big endian)
- add example ieee double
- check byte/nibble atributions
+ unit test suite
+ test under windows
+ test under a solaris sparc box (big endian)
+ add example ieee double
+ check byte/nibble atributions
'''
from struct import pack,unpack
import sys
@@ -149,35 +149,37 @@
'''
result = None
+
+ # check finite
if isFinite(float):
- positive = isPositive(float)
- denormalised = isDenormalised(float)
- if denormalised(float):
- if isZero():
- if positive:
- result = CLASS_POS_ZERO
- else:
- result = CLASS_NEG_ZERO
- else:
- if positive:
- result = CLASS_POS_DENORMAL
- else:
- result = CLASS_NEG_DENORMAL
- else:
- if positive:
- result = CLASS_POS_NORMAL
- else:
- result = CLASS_NEG_NORMAL
+ # check and store is positive
+ positive = isPositive(float)
+ if isZero():
+ if positive:
+ result = CLASS_POS_ZERO
+ else:
+ result = CLASS_NEG_ZERO
+ elif isDenormalised(float):
+ if positive:
+ result = CLASS_POS_DENORMAL
+ else:
+ result = CLASS_NEG_DENORMAL
+ else:
+ if positive:
+ result = CLASS_POS_NORMAL
+ else:
+ result = CLASS_NEG_NORMAL
else:
- if isNaN(float):
- # we don't currently test the type of nan signalling vs quiet
- result = CLASS_QUIET_NAN
- elif isPosInf(float):
- result = CLASS_POS_INF
- elif isNegInf(float):
- result = CLASS_NEG_INF
-
-
+ if isNaN(float):
+ # we don't currently test the type of nan signalling vs quiet
+ # so we always assume a quiet nan
+ result = CLASS_QUIET_NAN
+ elif isPosInf(float):
+ result = CLASS_POS_INF
+ elif isNegInf(float):
+ result = CLASS_NEG_INF
+
+
def packBytesAsPyFloat(bytes):
''' pack 8 bytes into a python float
@@ -191,12 +193,16 @@
returns -- a python float
throws -- an Exception if bytes contains < 8 bytes
- type of exception not determined
- '''
-
+ type of exception not determined
+ '''
+ # pack bytes into binary string
doubleString=pack('8B',*bytes)
+
+ #change byte order to little endian by reversing string
if sys.byteorder == 'big':
doubleString = doubleString[::-1]
+
+ # unpack binary string to a python float
return unpack('d',doubleString)[0]
@@ -241,11 +247,15 @@
if not isinstance(obj,float):
raise TypeError('the object recieved wasn\'t a float, type was: %s' % type(obj))
+ # pack float into binary string
packed =pack('d',obj)
+
+ #change byte order to little endian by reversing string
if sys.byteorder == 'big':
- packed = packed[::-1]
+ packed = packed[::-1]
+
return packed
-
+
def floatAsByteArray(obj):
''' unpack a python float as a list of 8 bytes
@@ -259,8 +269,15 @@
throws -- throws an exception if obj is not composed of 8 bytes
'''
- return list(unpack('8B',floatToBinaryString(obj)))
-
+ #unpack bytes to a binary string (takes care of byte order)
+ binaryString = floatToBinaryString(obj)
+
+ # convert the binary string to an array of 8 bytes
+ bytes = unpack('8B',binaryString)
+
+ #convert bytes to a list for ease of editing
+ return list(bytes)
+
def getSignBit(obj):
@@ -269,12 +286,16 @@
obj -- a python float object
returns -- the floats sign bit, this has the value 1 if the float is
- negative otherwise 0 (positive)
+ negative otherwise 0 (positive)
throws -- throws a TypeError if the the input object isn't a python float
-
+
'''
+
+ # unpack float to bytes
unpacked = floatAsByteArray(obj)
+
+ # grab last byte and check if sign bit is set
return unpacked[7] & SIGNBIT
def isPositive(obj):
@@ -285,14 +306,14 @@
returns -- True if the float is positive otherwise False
throws -- throws a TypeError if the the input object isn't a python float
-
+
'''
- unpacked = floatAsByteArray(obj)
+
if getSignBit(obj):
return False
else:
return True
-
+
def isNegative(obj):
''' test if a a pyhton float 64 bit ieee-74 double is negative
@@ -318,8 +339,12 @@
throws -- throws a TypeError if the the input objects aren't a python floats
'''
+
+ # check to see if objects are nans
nanTest1 = isNaN(obj1)
nanTest2 = isNaN(obj2)
+
+ # if either object is a nan we are unordered
if nanTest1 or nanTest2:
return True
else:
@@ -337,9 +362,10 @@
throws -- throws a TypeError if the the input object isn't a python float
'''
+
result = True
if isNaN(obj):
- result = False
+ result = False
if isInf(obj):
result = False
@@ -356,26 +382,32 @@
toDouble -- the python float to copy the sign bit to
throws -- throws a TypeError if toDouble isn't a python float or if
- fromNumber can't be converted to a float
-
- '''
+ fromNumber can't be converted to a float
+
+ '''
+
+ #convert first number to a float so as to use facilities
fromNumber = float(fromNumber)
+
+ # check signs of numbers
fromIsPositive = isPositive(fromNumber)
toIsPositive = isPositive(toDouble)
+
+ # convert the float to an array of 8 bytes
toBytes = floatAsByteArray(toDouble)
+
if not toIsPositive and fromIsPositive:
- toBytes[7] &= 0x7F
+ # unset the sign bit of the number
+ toBytes[7] &= EXPONENT_SIGN_MASK
+
elif toIsPositive and not fromIsPositive:
- toBytes[7] = toBytes[7] + 128
+ # set the sign bit
+ toBytes[7] = toBytes[7] + 0x80
+
+ #repack bytes to float
return packBytesAsPyFloat(toBytes)
-
-
-
-
-
-
-
+
def isDenormalised(obj):
''' check to see if a python float is denormalised
@@ -388,15 +420,22 @@
result -- True if the number is denormalised
throws -- throws a TypeError if toDouble isn't a python float or if
- obj isn't a float
- '''
+ obj isn't a float
+ '''
+
+ result = True
+ # check to see if the exponent is all zeros (a denorm doesn't have a
+ # finite exponent) Note we ignore the sign of the float
if not isExpAllZeros(obj):
- return False
- manBytes = getMantissaBytes(obj)
- for byte in manBytes:
- if byte > 0:
- return True
- return False
+ result = False
+
+ # check to see if this is zero (which is in some ways a special
+ # class of denorm... but isn't counted in this case)
+ # if it isn't zero it must be a 'general' denorm
+ if isZero(obj):
+ result = False
+
+ return result
@@ -418,11 +457,15 @@
throws -- throws a TypeError if obj isn't a python float
'''
- unpacked = floatToBinaryString(obj)
- bytes = list(unpack('8B',unpacked))
+
+ # unpack float to bytes
+ bytes = floatAsByteArray(obj)
+
+ # mask out overlap from exponent
bytes[6] = bytes[6] & MANTISSA_NIBBLE_MASK
- bytes=bytes[:7]
- return bytes
+
+ # remove the exponent bytes that can be removed
+ return bytes[:7]
def getExponentBytes(obj):
''' get the 2 bytes that makeup the exponent of a float
@@ -440,13 +483,17 @@
throws -- throws a TypeError if obj isn't a python float
'''
- unpacked = floatToBinaryString(obj)
- bytes = list(unpack('8B',unpacked))
+ # unpack float to bytes
+ bytes = floatAsByteArray(obj)
+
+ # mask out the ovberlap with the mantissa
bytes[6] = bytes[6] & EXPONENT_NIBBLE_MASK
+
+ # mask out the sign bit
bytes[7] = bytes[7] & EXPONENT_SIGN_MASK
- bytes=bytes[6:]
-
- return bytes
+
+ # remove the mantissa bytes that can be removed
+ return bytes[6:]
@@ -460,10 +507,16 @@
throws -- throws a TypeError if obj isn't a python float
'''
+ result = True
+
+ # get the exponent as a byte array porperly masked
expBytes = getExponentBytes(obj)
+
+ # check to see if any of the bytes in the exponent are not zero
if expBytes[0] > 0 or expBytes[1] > 0:
- return False
- return True
+ result = False
+
+ return result
def isMantissaAllZeros(obj):
''' check if the bits of the mantissa of a float is zero
@@ -474,14 +527,19 @@
throws -- throws a TypeError if obj isn't a python float
'''
+ result = True
+
+ # get the mantissa as a byte array properly masked
mantissaBytes = getMantissaBytes(obj)
- result = True
+
+ # check if any of the mantissa bytes are greater than zero
for byte in mantissaBytes:
if byte != 0:
result = False
break
+
return result
-
+
def isExpAllOnes(obj):
''' check if the bits of the exponent of a floatis all 1 bits
@@ -491,10 +549,17 @@
throws -- throws a TypeError if obj isn't a python float
'''
+
+ result = False
+
+ # get the exponent as a byte array properly masked
expBytes = getExponentBytes(obj)
+
+ # check against masks to see if all the correct bits are set
if expBytes[0] == EXPONENT_ALL_ONES_BYTE_0 and expBytes[1] == EXPONENT_ALL_ONES_BYTE_1:
- return True
- return False
+ result = True
+
+ return result
def isNaN(obj):
''' check to see if a python float is an ieee-754 double not a number (nan)
@@ -505,17 +570,29 @@
throws -- throws a TypeError if obj isn't a python float
'''
-
+
+ # bad result for code checking
+ result = None
+
+ # check to see if exponent is all ones (excluding sign bit)
+ # if exponent is not all ones this can't be a NaN
if not isExpAllOnes(obj):
- return False
- manBytes = getMantissaBytes(obj)
- manNan = False
- for byte in manBytes[:6]:
- if byte > 0:
- manNan = True
- if (manBytes[6] & MANTISSA_NIBBLE_MASK) > 0:
- manNan = True
- return manNan
+ result = False
+ else:
+ # get the mantissa as a byte array properly masked
+ manBytes = getMantissaBytes(obj)
+
+ # check if any of the unmasked mantissa bytes are not zero
+ # to be a NaN the mantissa must be non zero
+ for byte in manBytes:
+ if byte > 0:
+ result = True
+ break
+ # todo NOT NEEDED, UNITTEST!!!!
+ # check to see if the mantissa nibble that overlaps with the
+ #if (manBytes[6] & MANTISSA_NIBBLE_MASK) > 0:
+ # result = True
+ return result
def isInf(obj):
''' check to see if a python float is an infinity
@@ -528,10 +605,20 @@
throws -- throws a TypeError if obj isn't a python float
'''
+ # bad result for code checking
+ result = None
+
+ # check to see if exponent is all ones (excluding sign bit)
+ # if exponent is not all ones this can't be a Inf
if not isExpAllOnes(obj):
- return False
+ result = False
+
+ # get the mantissa as a byte array properly masked
manBytes = getMantissaBytes(obj)
- for i,byte in enumerate(manBytes):
+
+ for byte in manBytes:
+ #check if any of the unmasked mantissa bytes are zero
+ # to be a NaN the mantissa must be zero
if byte > 0:
return False
return True
@@ -559,9 +646,9 @@
'''
return isInf(obj) and not isPositive(obj)
-
-
-
-
-
-
+
+
+
+
+
+
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic