Python 32/64-битное машинное суммирование с плавающей точкой транспонированной матрицы не правильно? - PullRequest
2 голосов
/ 12 января 2012

Во-первых, я не математик, поэтому точность большого числа редко попадает в мою повседневную работу. Пожалуйста, будьте нежны. ;)

Использование NumPy для генерации матрицы со значениями, равными 1:

>>> m = numpy.matrix([(1.0 / 1000) for x in xrange(1000)]).T
>>> m
matrix[[ 0.001 ],
       [ 0.001 ],
       ...
       [ 0.001 ]])

В 64-битной Windows с Python 2.6 суммирование редко получается до 1,0. math.fsum () работает с этой матрицей, но если я изменяю матрицу для использования меньших чисел, этого не происходит.

>>> numpy.sum(m)
1.0000000000000007
>>> math.fsum(m)
1.0
>>> sum(m)
matrix([[ 1.]])
>>> float(sum(m))
1.0000000000000007

В 32-битном Linux (Ubuntu) с Python 2.6 суммирование всегда получается до 1,0.

>>> numpy.sum(m)
1.0
>>> math.fsum(m)
1.0
>>> sum(m)
matrix([[ 1.]])
>>> float(sum(m))
1.0000000000000007

Я могу добавить эпсилон к своему коду при оценке, если сумма матрицы равна 1 (например, -epsilon <сумма (м) <+ epsilon), но сначала я хочу понять, в чем причина различий в Python, и если есть лучший способ правильно определить сумму. </p>

Насколько я понимаю, сумма (-ы) обрабатывают машинное представление чисел (чисел с плавающей запятой) иначе, чем их отображение, и при суммировании используется внутреннее представление. Однако, глядя на 3 метода, которые я использовал для расчета суммы, не ясно, почему они все разные или одинаковые между платформами.

Какой лучший способ правильно рассчитать сумму матрицы?

Если вы ищете более интересную матрицу, это простое изменение будет иметь меньшие номера матрицы:

>>> m = numpy.matrix([(1.0 / 999) for x in xrange(999)]).T

Заранее спасибо за любую помощь!

Update Я думаю, что я что-то понял. Если я исправлю значение, хранящееся в 32-битной переменной, результаты будут соответствовать 32-битной сумме в Linux.

>>> m = numpy.matrix([(numpy.float32(1.0) / 1000) for x in xrange(1000)]).T
>>> m
matrix[[ 0.001 ],
       [ 0.001 ],
       ...
       [ 0.001 ]])
>>> numpy.sum(m)
1.0

Это установит, что номера машин матрицы будут представлять 32-разрядные числа с плавающей запятой, а не 64-разрядные в моем тесте Windows, и будут суммироваться правильно. Почему число с плавающей запятой 0,001 не равно номеру машины в 32-разрядной и 64-разрядной системах? Я ожидал бы, что они будут другими, если я пытаюсь хранить очень маленькие числа с большим количеством десятичных знаков.

У кого-нибудь есть мысли по этому поводу? Должен ли я в этом случае явно переключаться на 32-разрядные числа с плавающей запятой, или существует 64-разрядный метод суммирования? Или я вернулся к добавлению эпсилона? Извините, если я звучу глупо, мне интересны мнения. Спасибо!

Ответы [ 3 ]

2 голосов
/ 12 января 2012

Во-первых, если вы используете numpy для хранения значений, вы должны использовать методы numpy, если они есть, для работы с массивом / матрицей. То есть, если вы хотите довериться чрезвычайно способным людям, которые объединились.

Теперь 64-битный ответ numpy's sum () не может суммировать в точности до 1 по причинам, по которым числа с плавающей запятой обрабатываются в компьютерах (murgatroid99 предоставил вам ссылку, их там сотни и более). Следовательно, единственный безопасный способ (и даже очень полезный для понимания вашей математической обработки вашего кода намного лучше, и, следовательно, вашей проблемы как таковой) - это использовать значение epsilon, чтобы обрезать с определенной точностью.

Почему я думаю, что это полезно? Поскольку вычислительной науке приходится иметь дело с ошибками в той же степени, что и экспериментальная наука, и преднамеренно решая (то есть определяя их) ошибки в этом месте, вы уже сделали первый шаг в устранении вычислительных ошибок вашего кода.

Так что, возможно, есть и другие способы решения этой проблемы, но в большинстве случаев я бы использовал эпсилон для определения точности, необходимой для данной задачи.

2 голосов
/ 12 января 2012

Это потому, что вы сравниваете 32-разрядные операции с 64-разрядными операциями, как вы уже выяснили.

Если вы укажете 32-разрядный или 64-разрядный тип dtype на обеих машинах, выВы увидите тот же результат.

Тип по умолчанию с плавающей точкой Numpy (числовой тип для массива Numpy) совпадает с точностью машины.Вот почему вы видите разные результаты на разных машинах.

Например, 32-битная версия:

m = numpy.ones(1000, dtype=numpy.float32) / 1000
print repr(m.sum())

и 64-битная версия:

m = numpy.ones(1000, dtype=numpy.float64) / 1000
print repr(m.sum())

Будет отличаться из-за разной точности, но вы увидите одинаковые результаты на разных машинах.(Однако 64-разрядная операция будет на намного медленнее на 32-разрядной машине)

Если вы просто укажете numpy.float, это будет либо float32, либо float64 в зависимости от собственной архитектуры машины.

2 голосов
/ 12 января 2012

Я бы сказал, что самый точный (не самый эффективный) способ - использовать десятичный модуль :

>>> from decimal import Decimal
>>> m = numpy.matrix([(Decimal(1) / 1000) for x in xrange(1000)])
>>> numpy.sum(m)
Decimal('1.000')
>>> numpy.sum(m) == 1.0
True
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...