#!/usr/bin/env python ############################################################################# # Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1997, 1998, 1999 # All Rights Reserved. # # The software contained on this media is the property of the DSTC Pty # Ltd. Use of this software is strictly in accordance with the # license agreement in the accompanying LICENSE.HTML file. If your # distribution of this software does not contain a LICENSE.HTML file # then you have no rights to use this software in any manner and # should contact DSTC at the address below to determine an appropriate # licensing arrangement. # # DSTC Pty Ltd # Level 7, GP South # Staff House Road # University of Queensland # St Lucia, 4072 # Australia # Tel: +61 7 3365 4310 # Fax: +61 7 3365 4311 # Email: enquiries@dstc.edu.au # # This software is being provided "AS IS" without warranty of any # kind. In no event shall DSTC Pty Ltd be liable for damage of any # kind arising out of or in connection with the use or performance of # this software. # # Project: Fnorb # File: $Source: /cvsroot/fnorb/fnorb/orb/Fixed.py,v $ # Version: @(#)$RCSfile: Fixed.py,v $ $Revision: 1.4 $ # ############################################################################# """ Implementation of CORBA 'fixed' type (fixed point decimals). ** Warning ** This is just a start - it is in nowhere near complete, and is just intended to get the IDL parsed - the Fnorb runtime does not handle the 'fixed' type. Even arithmetic for constant expressions isn't handled!!!!!! ************* """ # Standard/built-in modules. import string, types # Fnorb modules. import CORBA _powers = None def powers(): global _powers if _powers is None: _powers = [1l] for i in range(62): _powers.append(_powers[-1]*10) return _powers class Fixed: """ Implementation of CORBA 'fixed' type (fixed point decimals). """ def __init__(self, precision, decimals = None, value=None): """ Constructor. 'precision' is the number of significant digits. 'decimals' is the number of decimals. 'value' is an integer value containing the significant digits. """ if decimals is None: # Only a single argument. Must be a string if not isinstance(precision, types.StringType): raise CORBA.DATA_CONVERSION self._fnorb_from_literal(precision) return if value is not None: if isinstance(value, types.StringType): self._fnorb_from_literal(value) # Adjust value to target scale while decimals < self.__decimals: d,r = divmod(self.__value, 10) if r != 0: raise CORBA.DATA_CONVERSION self.__value = d self.__decimals -= 1 while decimals > self.__decimals: self.__value *= 10 self.__decimals += 1 self.__precision = precision else: self.__value = value self.__precision = precision self.__decimals = decimals # Verify that value is representable if self.__value > powers()[self.__precision]: raise CORBA.DATA_CONVERSION else: # Value will be inserted during unmarshalling self.__precision = precision self.__decimals = decimals return ######################################################################### # Methods for arithmetic operations! ######################################################################### def __add__(self, other): """ Addition ;^) """ other = self.__check_other(other) val = self.__value oval = other.__value decimals = self.__decimals if other.__decimals < decimals: oval *= 10 ** (decimals - other.__decimals) elif decimals < other.__decimals: val *= 10 ** (other.__decimals - decimals) decimals = other.__decimals precision = max(self.__precision - self.__decimals, other.__precision - other.__decimals) + \ max(self.__decimals, other.__decimals) + 1 return Fixed(precision, decimals, val + oval).__strip() def __sub__(self, other): """ Subtraction ;^) """ other = self.__check_other(other) val = self.__value oval = other.__value decimals = self.__decimals if other.__decimals < decimals: oval *= 10 ** (decimals - other.__decimals) elif decimals < other.__decimals: val *= 10 ** (other.__decimals - decimals) decimals = other.__decimals precision = max(self.__precision - self.__decimals, other.__precision - other.__decimals) + \ max(self.__decimals, other.__decimals) + 1 return Fixed(precision, decimals, val - oval).__strip() def __mul__(self, other): """ Multiplication ;^) """ other = self.__check_other(other) return Fixed(self.__precision + other.__precision, self.__decimals + other.__decimals, self.__value * other.__value).__strip() def __div__(self, other): """ Division ;^) """ other = self.__check_other(other) self = self.__strip() val = self.__value precision = self.__precision decimals = self.__decimals # widen to 62 digits decimals += 62 - precision val *= powers()[62 - precision] precision = 62 other = other.__strip() val /= other.__value decimals -= other.__decimals while val < powers()[precision-1]: precision -= 1 # truncate to less than 32 digits while precision > 32: val /= 10 precision -= 1 decimals -= 1 return Fixed(precision, decimals, val).__strip() def __neg__(self): """ Unary '-' """ return Fixed(self.__precision, self.__decimals, -self.__value) def __pos__(self): """ Unary '+' """ return self ######################################################################### # CORBA interface. ######################################################################### def value(self): """ Return the significant digits as an integer. """ return self.__value def precision(self): """ Return the number of significant digits. """ return self.__precision def decimals(self): """ Return the number of decimals. """ return self.__decimals def __eq__(self, other): """ Determine whether two Fixed instances are equal. """ if not isinstance(other, Fixed): return 0 if self.__value != other.__value: return 0 if self.__precision != other.__precision: return 0 if self.__decimals != other.__decimals: return 0 return 1 def __ne__(self, other): """ Determine whether two Fixed instances are not equal. """ return not self.__eq__(other) def __str__(self): val = self.__value if val < 0: sign = "-" val = -val else: sign = "" val = str(val) if val[-1] == 'L': val = val[:-1] if self.__decimals <= 0: # trailing zeroes return sign + val + "0" * (-self.__decimals) elif len(val) > self.__decimals: # just a decimal dot return sign + val[:-self.__decimals] + "." + val[-self.__decimals:] else: # leading zeroes return sign + "0." + "0" * (self.__decimals - len(val)) + val ######################################################################### # Fnorb-specific interface. ######################################################################### def _fnorb_from_literal(self, literal): """ Initialise the instance from a literal. """ # Is there a decimal point? point = string.find(literal, '.') # Nope! if point == -1: significant = literal[:-1] # The '-1' drops the 'd' or 'D'. fraction = '' # Yep! else: significant = literal[:point] fraction = literal[point+1:-1] # The '-1' drops the 'd' or 'D'. # Concatenate all of the (possibly!) significant digits. significant = significant + fraction # Count the leading and trailing zeroes. leading = self.__count_leading_zeroes(significant) trailing = self.__count_trailing_zeroes(significant) # Strip the leading and trailing zeroes and evalute the string to get # the integer representation. self.__value = eval(significant[leading:len(significant) - trailing]) # Set the number of significant digits. self.__precision = len(str(self.__value)) # Set the number of decimals. self.__decimals = len(fraction) - trailing return def _fnorb_typecode(self): """ Return a typecode for the instance. """ return CORBA.FixedTypeCode(self.__precision, self.__decimals) def _fnorb_marshal(self, cursor, digits, scale): """ Marshal myself onto an octet stream. """ val = self.__value decimals = self.__decimals # Adjust value to target scale while scale < decimals: d,r = divmod(val, 10) if r != 0: raise CORBA.DATA_CONVERSION val = d decimals -= 1 while scale > decimals: val *= 10 decimals += 1 # Verify that value is representable if val > powers()[digits]: raise CORBA.DATA_CONVERSION nbytes = (digits + 2) / 2 sign = val < 0 val = abs(val) nibbles = [0] * (2 * nbytes) if sign: nibbles[-1] = 0xD else: nibbles[-1] = 0xC # Marshal data for i in range(2, 2 * nbytes + 1): if val == 0: break nibbles[-i] = val % 10 val /= 10 bytes = [None] * nbytes for i in range(nbytes): bytes[i] = chr(nibbles[2*i]*16 + nibbles[2*i+1]) bytes = ''.join(bytes) cursor.marshal('S', bytes) def _fnorb_unmarshal(self, cursor): """ Unmarshal myself from an octet stream. """ nbytes = (self.__precision + 2) / 2 bytes = cursor.unmarshal(('S',nbytes)) nibbles = [None] * (2 * nbytes) for i in range(nbytes): nibbles[2*i] = ord(bytes[i]) >> 4 nibbles[2*i+1] = ord(bytes[i]) & 15 val = 0l for i in range(2 * nbytes - 1): val = 10 * val + nibbles[i] if nibbles[-1] == 0xD: val = -val self.__value = val ######################################################################### # Private interface. ######################################################################### def __count_leading_zeroes(self, s): """ Count the leading zeroes in the string 's'. """ count = 0 for i in range(len(s)): if s[i] != '0': break count = count + 1 return count def __count_trailing_zeroes(self, s): """ Count the trailing zeroes in the string 's'. """ count = 0 for i in range(len(s) - 1, -1, -1): if s[i] != '0': break count = count + 1 return count def __check_other(self, other): if isinstance(other, int) or isinstance(other, long): return Fixed(31,0,other).__strip() if isinstance(other, Fixed): return other raise TypeError, "Invalid fixed point value" def __strip(self): changed = 0 val = self.__value decimals = self.__decimals precision = self.__precision # strip trailing zeroes while decimals > 0: d,r = divmod(val, 10) if r != 0: break val = d decimals -= 1 changed = 1 # strip leading zeroes while precision > 0 and powers()[precision-1] > abs(val): precision -= 1 changed = 1 if not changed: return self return Fixed(precision, decimals, val) ############################################################################# # Testing. if __name__ == '__main__': import new, sys f = new.instance(Fixed, {}) f._fnorb_from_literal(sys.argv[1]) print 'Typecode:', f._fnorb_typecode().__dict__ #############################################################################