一旦数据进入Cython modu,精度就会丢失/改变

2024-06-16 12:14:12 发布

您现在位置:Python中文网/ 问答频道 /正文

我将一些使用NumPy的代码移植到Cython中以获得一些性能提升。我取得了相当大的进步,但我遇到了一个问题。在

Cython得到的结果与Python得到的结果不同。我不知道为什么会发生这样的事情,所以我决定看看是什么被推到了赛顿模块。在

在它到达Cython之前,数据看起来像:

azimuth = 0.000349065850399 

rawDistance = [ 2.682  7.234  2.8    7.2    2.912  7.19   3.048  7.174  3.182  7.162
  3.33   7.164  3.506  7.158  3.706  7.154  3.942  7.158  4.192  7.158
  4.476  7.186  4.826  7.19   5.218  7.204  5.704  7.224  6.256  7.248
  6.97   7.284] 

intensity = [19 34 25 28 26 48 21 56 21 60 31 49 24 37 26 37 34 37 23 84 15 59 23 45 
             18  47 20 55 18 36 15 39]

一旦进入Cython,同样的数据看起来像:

^{pr2}$

这就解释了为什么结果与纯Python方法计算的结果不完全相同。在

这是将信息传输到的Cython模块:

from libc.math cimport sin, cos
import numpy as np
cimport numpy as np
cimport cython

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
def calculateXYZ(list frames, double[:] cosVertCorrection, double[:] sinVertCorrection):
    cdef long numberFrames = len(frames)
    cdef long i, j, k, numberBlocks
    cdef list finalResults = []
    cdef list intensities = []
    cdef list frameXYZ = []
    cdef double azimuth, xy, x, y, z, sinRotational, cosRotational
    cdef double[32] rawDistance
    cdef int[32] intensity
    cdef double[:] tempX
    cdef double[:] tempY
    cdef double[:] tempZ
    cdef int positionsFilled = 0

    for i in xrange(numberFrames):
        numberBlocks = len(frames[i])
        tempX = np.zeros(numberBlocks * 32, dtype=np.double)
        tempY = np.zeros(numberBlocks * 32, dtype=np.double)
        tempZ = np.zeros(numberBlocks * 32, dtype=np.double)
        frameXYZ = [[] for i in range(3)]
        positionsFilled = 0

        for j in xrange(numberBlocks):
            # This is where I tested for the data in Cython
            # This is the information that is different. 
            # It is reading from what was passed to it from python.

            azimuth = frames[i][j][0]
            rawDistance = frames[i][j][1]
            intensity = frames[i][j][2]
            sinRotational, cosRotational = sin(azimuth), cos(azimuth)

            for k in xrange(32):
                xy = rawDistance[k] * cosVertCorrection[k]
                x, y = xy * sinRotational, xy * cosRotational
                z = rawDistance[k] * sinVertCorrection[k]

                if x != 0 or y != 0 or z != 0:
                    tempX[positionsFilled] = x
                    tempY[positionsFilled] = y
                    tempZ[positionsFilled] = z
                    intensities.append(intensity[k])
                    positionsFilled = positionsFilled + 1

        frameXYZ[0].append(np.asarray(tempX[0:positionsFilled].copy()).tolist())
        frameXYZ[1].append(np.asarray(tempY[0:positionsFilled].copy()).tolist())
        frameXYZ[2].append(np.asarray(tempZ[0:positionsFilled].copy()).tolist())
        finalResults.append(frameXYZ)

    return finalResults, intensities

这是它的纯Python版本:

documentXYZ = []
intensities = []

# I tested to see what the original data was in here adding prints

for frame in frames:
    frameXYZ = [[] for i in range(3)]
    frameX, frameY, frameZ = [], [], []
    for block in frame:
        sinRotational, cosRotational = np.math.sin(block[0]), np.math.cos(block[0])
        rawDistance, intensity = np.array(block[1]), np.array(block[2])
        xy = np.multiply(rawDistance, cosVertCorrection)
        x, y, z = np.multiply(xy, sinRotational), np.multiply(xy, cosRotational), np.multiply(rawDistance, sinVertCorrection)
        maskXYZ = np.logical_and(np.logical_and(x, x != 0), np.logical_and(y, y != 0), np.logical_and(z, z != 0))
        frameX += x[maskXYZ].tolist()
        frameY += y[maskXYZ].tolist()
        frameZ += z[maskXYZ].tolist()
        intensities += intensity[maskXYZ].tolist()

    frameXYZ[0].append(frameX), frameXYZ[1].append(frameY), frameXYZ[2].append(frameZ)
    documentXYZ.append(frameXYZ)

我理解浮点值的精度可能存在差异(尽管我认为不应该如此,因为我在所有结构中都使用doubles),但我不明白为什么作为整数的intensity值也会发生变化。我希望精度与Python相同。在

有什么改进的办法吗?在

谢谢。在


Tags: inforframesnpcythondoublexyappend
1条回答
网友
1楼 · 发布于 2024-06-16 12:14:12

解决问题的前两个步骤是:

  1. 确定平台上NumPy使用的特定整数类型(例如int32int64…),例如通过检查整数数组的dtype属性或其值之一。

  2. 使用所选的C实现确定平台上int的位宽度。通常是32位,但不总是这样(例如用sizeof检查)。

一旦了解了这两个细节,就可以确定普通(C)int与NumPy一直使用的整数精度不匹配的方式。一个常见的猜测是NumPy使用int64,但在C中使用的是int,这很可能是您的平台/实现的int32。另一个常见的情况是NumPy使用无符号整数,而在C中int将有符号,即使具有相同的位数,也会导致不同的表示。在

您可以在Cython中轻松引用固定宽度整数,至少有以下三种方法:

  1. 因为您使用了cimport numpy as np,所以可以引用NumPy的固定宽度整数类型,例如np.int64_t或{}。“t”typedef在NumPy的Cython支持中可用。

  2. 您可以尝试从C实现和平台中计算出一个标准类型名,例如,cython.longlong表示64位整数,cython.uchar表示无符号8位整数,这正好对应于正确的位数和整数的正确有符号性,以匹配使用过的任何类型的精度和有符号性通过纽比。

  3. 如果您喜欢将C的标准头文件用于指定大小的固定宽度整数,也可以从C标准库导入,例如from libc.stdint import int64_t, uint8_t

假设您已经选择了适当的整数类型,那么您就可以用正确的类型声明您的intensity数组,例如,根据您选择的表示正确整数类型的方法,可以执行以下任何操作:

cdef np.uint8_t[32] intensity   # If using NumPy integer types
cdef uint8_t[32] intensity      # If importing from libc.stdint
cdef cython.uchar[32] intensity # If using Cython integer types

最后一点要注意的是,常规的Python整数是无限精度的,因此,如果您设法获得一个由int类型组成的NumPy数组(不是Cint,而是Pythonint),那么在使用Cython时,您必须决定一个不同的、固定精度的表示形式,或者使用包含Python int类型的数组或类型化内存视图(这通常会导致首先无法使用Cython)。在

相关问题 更多 >