Numpy Vectorization - странная проблема - PullRequest
0 голосов
/ 21 февраля 2019

Я выполняю некоторые векторизованные вычисления, используя numpy.Я исследовал имеющуюся ошибку и закончил этой строкой:

(vertices[:,:,:,0]+vertices[:,:,:,1]*256)*4

Ожидается, что для индекса vertices[0,0,17] результат будет 100728, однако я получаю 35192.Когда я попытался изменить его на 4.0 вместо 4, я закончил получать правильное значение 100728 и, таким образом, исправил свою ошибку.

Я хотел бы понять, почему с плавающей запятой здесь особенно важночто я использую Python 3.7, и это умножение, даже не деление.

Дополнительная информация:

vertices.shape=(203759, 12, 32, 3)
python==3.7
numpy==1.16.1

Редактировать 1:

  • тип вершин - "numpy.uint8"
  • вершин [0, 0,17] => [94, 98, 63]

1 Ответ

0 голосов
/ 21 февраля 2019

Проблема здесь в том, что вы используете слишком маленькие целые числа, и число переполняется и оборачивается, потому что numpy использует целые числа фиксированной ширины, а не бесконечной точности, как в python int.Numpy будет "продвигать" тип результата на основе входных данных, но не будет продвигать результат на основании того, происходит ли переполнение или нет (это делается до фактического расчета.

В этом случае, когда вы умножаете: vertices[:,:,:,1]*256 (я назову это A), 256 не может удерживаться в uint8, поэтому он переходит к следующему более высокому типу: uint16 это позволяет результат умножениячтобы сохранить правильное значение в этом случае, потому что максимально возможное значение любого элемента в verticies равно 255, поэтому самое большое возможное значение составляет 255 * 256, что прекрасно вписывается в 16-битную единицу.

Затем вы добавляете vertices[:,:,:,0] + A (я назову это B). Если наибольшее значение A было 255 * 256, а наибольшее значение vertices[:,:,:,0] равно 255 (опять же самое большое значение uint8)наибольшая сумма двух равна 2 16 -1 (наибольшее значение, которое вы можете хранить в 16-битном целом без знака). Это все еще хорошо, пока вы не перейдете к последнему умножению.

Когда вы доберетесь до B * 4, снова наберетсяЧтобы определить тип возвращаемого значения.Целое число 4 легко помещается в uint16, поэтому numpy не переводит тип еще выше в uint32 или uint64, потому что оно не предотвращает переполнения, как описано выше.Это приводит к тому, что любые продукты умножения, превышающие 2 16 -1, возвращаются по модулю 2 16 .

Если вместо этого вы используете число с плавающей запятой (4. or 4.0)numpy рассматривает это как тип «более высокого» значения, который не может поместиться в uint16, поэтому он переводит результат в число с плавающей запятой, которое может вместить гораздо более высокие числа без переполнения.

Если вы не хотитечтобы изменить весь массив: verticies на больший тип d, вы можете просто взять результат B и преобразовать его перед умножением на 4 следующим образом: B.astype(np.uint64) * 4.Это позволит вам хранить намного больших значений без переполнения (хотя на самом деле это не устраняет проблему, если значение больше 4).

...