十进制类隐式类型转换?

时间:2017-04-28 03:05:32

标签: python

作为学习python的练习,我试图构建一个完全实现的类型向量。

在实现这一点时,我很快就遇到了对精度进行更多控制的需求,并且通常对于一个体面的类型更加精确,我将在后面应用于更通用的线性代数库。为此,我目前正在使用内置的Decimal类。

我遇到了我认为在方法大小和规范化之间的关系中的奇怪性。本质上,幅度方法的返回类型是将完全使用十进制对象的操作转换为浮点对象。当我尝试将十进制对象除以此返回值时,这种不一致会在规范化方法中引发类型错误。

我已经注释了一个我提出的hacky解决方案,我只是明确强制将返回值强制为Decimal对象。我对这种方法的问题是我希望在这个课程中保持数学精度的潜在损失。到目前为止,我还没有准确确定这种隐式类型转换的确切位置,更不用说如何最好地处理维持数学精度的解决方案。

可能违规行:

#65    return (math.sqrt(sum(coordinates_squared)))

矢量类:

import math
from decimal import Decimal, getcontext
GLOBAL_PRECISION = 30
getcontext().prec = GLOBAL_PRECISION

class Vector(object):
    """The Vector class takes an iterable object, and stores it as a
    a mathematical vector. Class defines several vector operations.
    """
    CANNOT_NORMALIZE_ZERO_VECTOR_MSG = 'Cannot normalize the zero vector'

    def __init__(self, coordinates):
        """The variable coordinates must be an iterable object, whose
        elements are numberic, and valid input for Decimal class
        """
        try:
            if not coordinates:
                raise ValueError
            self.coordinates = tuple([Decimal(x) for x in coordinates])
            self.dimension = len(self.coordinates)

        except ValueError:
            raise ValueError('The coordinates must be nonempty')

        except TypeError:
            raise TypeError('The coordinates must be an iterable')

    def __str__(self):
        """Print a vector as a row vector"""
        return 'Vector: {}'.format(self.coordinates)

    def __eq__(self, v):
        """define vector equality as component-wise equality"""
        return self.coordinates == v.coordinates

    def __add__(self, v):
        assert( self.dimension == v.dimension ), 'Does not make sense to ' \
                'add vectors of different dimensions'

        _result = [ x+y for x,y in zip(self.coodinates, v.coodinates)]
        return (Vector(_result))

    def __sub__(self, v):
        assert( self.dimension == v.dimension ), 'Does not make sense to ' \
                'subtract vectors of different dimensions'

        _result = [ x-y for x,y in zip(self.coodinates, v.coodinates)]
        return (Vector(_result))

    """def __mul__(self, k):
        _result = [Decimal(k)*x for x in self.coordinates]
        return Vector(_result)
    """

    def scale_mul(self, k):
        """return a Vector object that has had each element scaled by k"""
        _result = [Decimal(k)*x for x in self.coordinates]
        return( Vector(_result) )

    def magnitude(self):
        """return a Decimal object that is the root of the sum of the
        squares of the elements of the vector self
        """
        coordinates_squared = [x**2 for x in self.coordinates]
        return (math.sqrt(sum(coordinates_squared)))
        #return Decimal((math.sqrt(sum(coordinates_squared))))


    def normalized(self):
        """returns a vector object that has been scaled by 1/self.magnitude"""
        try:
            return ( self.scale_mul(Decimal('1.0')/self.magnitude()) )

        except ZeroDivisionError:
            raise Exception(self.CANNOT_NORMALIZE_ZERO_VECTOR_MSG)

    def dot(self, v):
        """return the dot product of 2 vector objets as a Decimal object"""
        assert( self.dimension == v.dimension ), 'Does not make sense to ' \
                'dot vectors of different dimensions'

        return sum([x*y for x,y in zip(self.coordinates, v.coordinates)])

    def angleTheta(self, v, in_degrees = False):
        """returns the smaller of the two angles between two vectors,
        presuming the vectors eminate from the same initial position.
        Returns radians by default, can be flipped to degrees with
        additional true parameter
        """
        try:
            u1 = self.normalized()
            u2 = v.normalized()
            assert(abs(round(u1.dot(u2), 10)) <= 1), 'acos must recieve' \
                'parameter between 0 and 1'

            angle_in_radians = math.acos(round(u1.dot(u2), 10))

            if in_degrees:
                return math.degrees(angle_in_radians)
            else:
                return angle_in_radians

        except Exception as e:
            if str(e) == self.CANNOT_NORMALIZE_ZERO_VECTOR_MSG:
                raise Exception('Cannot compute an angle with the zero vector')
            else:
                raise e

    def is_parallel(self, v):
        """returns true if parameter v is parallel to the vector self"""
        return( self.is_zero() or
                v.is_zero() or
                self.angleTheta(v) == 0 or
                self.angleTheta(v) == math.pi )

    def is_orth(self, v, tolerance=1e-10):
        """returns true if parameter v is orthagonal to the vector self"""
        return ( abs(self.dot(v)) < tolerance )

    def is_zero(self, tolerance=1e-10):
        """returns true if self is the zero vector"""
        return (self.magnitude() < tolerance)

0 个答案:

没有答案