Вопрос по python, numerical – Арифметика Python с небольшими числами

6

Я получаю следующий неожиданный результат, когда делаю арифметику с небольшими числами в Python:

<code>>>> sys.float_info
sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)
>>> (1. - (1.e-17) ) < 1.
False
</code>

Я знаю, что числа с плавающей запятой не имеют бесконечной точности, но они должны уметь обрабатывать «большие» маленькие числа, такие как 1e-17, не так ли?

Ваш Ответ

7   ответов
8
>>> import numpy as np
>>> np.nextafter(1., 0.)
0.99999999999999989

1., в направлении0.

Похоже1. - 1.e-17 просто ближе к1. чемnumpy.nextafter(1., 0.), так когда1. - 1.e-17 оценивается, это дает вам 1 обратно. Не имеет смысла использовать какой-то другой поплавок, который был еще дальше.

Связанный вопрос -> Увеличивает значение с плавающей запятой Python на наименьшее возможное количество

Спасибо за то, что вы указали на numpy.nextafter () и объяснили, как его использовать! Это чрезвычайно полезно для того, что я пытаюсь сделать. user1389890
4

epsilon действительно в возвращаемом значенииsys.float_info.

Эпсилон (или) наименьшее число такое, что 0.5 + ≠ 0.5 И 0.5 - ≠ 0.5

Python говорит вам, что наименьшее число, которое приведет к0.5 увеличивать или уменьшать повторно - этоepsilon=2.220446049250313e-16 - но это только для значения 0,5. Вы пытаетесь увеличить1.0 по1.0e-17. Это большее значение (1,0 против 0,5), которое увеличивается на меньшее число, чем для 0,5 (1,0e-17 против 2,2e-16). Вы примерно на порядок отклонены, поскольку значение приращения 1.0e-17 на порядок меньше относительного эпсилона для 1.0.

Вы можете увидеть это здесь:

Они меняют значение0.5

>>> 0.5+sys.float_info.epsilon
0.5000000000000002
>>> 0.5-sys.float_info.epsilon
0.4999999999999998

Эти значения не:

>>> 0.5+sys.float_info.epsilon/10.0
0.5
>>> 0.5-sys.float_info.epsilon/10.0
0.5
>>> 5.0+sys.float_info.epsilon
5.0
>>> 5.0-sys.float_info.epsilon
5.0

Explanation:

IEEE 754 определяет формат с плавающей запятой, используемый сегодня на большинстве стандартных компьютеров (специализированные компьютеры или библиотеки могут использовать другой формат 64 битный формат IEEE 754 использует 53 бита точности для вычисления и 52 для сохранения в мантиссе значения с плавающей запятой. Поскольку у вас есть фиксированные 52/53 бит для работы, величина и точность мантиссы изменяется для больших / меньших значений. Таким образом, затем изменяется как относительная величина числа с плавающей точкой. Значение для 0,5 отличается от значения для 1,0 и для 100,0.

По ряду очень веских и специфичных для платформы причин (хранение и представление, округление и т. Д.), Даже если вымо используйте меньшее число, epsilon определяется как 52-битная точность для 64-битного формата с плавающей запятой. Поскольку в большинстве реализаций Python для поплавка используется двойной плавающий Си, это можно продемонстрировать:

>>> 2**-52==sys.float_info.epsilon
True

Смотри сколько битва платформа подойдет:

>>> 0.5 + 2.0**-53
0.5000000000000001
>>> 0.5 - 2.0**-53
0.4999999999999999
>>> 0.5 + 2.0**-54
0.5                           # fail for 0.5 + 54 bits...
>>> 0.5 - 2.0**-54
0.49999999999999994           # OK for minus
>>> 0.5 - 2.0**-55
0.5                           # fail  for 0.5 minus 55 bits...

Существует несколько способов решения вашей проблемы:

Вы можете использовать концепцию C99 Nextafter для расчета значения соответствующего эпсилона. Для Python используйте либо numpy, либо класс Decimal для вычисленияnextafter. Ещеnextafter в моем предыдущем ответеВО Используйте целые числа. 64-разрядное целое число будет четко обрабатывать значение эпсилона в 17-м порядке без округления. Используйте математическую библиотеку произвольной точности.Десятичны входит в стандартный дистрибутив Python.

Важной концепцией является то, что значение относительно значения (и если вы увеличиваете или уменьшаете).

Это можно увидеть здесь:

>>> numpy.nextafter(0.0,1.0)-0.0
4.9406564584124654e-324                 # a relative epsilon value of 4.94e-324
>>> numpy.nextafter(0.01,1.0)-0.01
1.7347234759768071e-18                  # 1e-17 would still work...
>>> numpy.nextafter(0.1,1.0)-0.1
1.3877787807814457e-17                  # 1e-17 would >>barely<< work...
>>> numpy.nextafter(0.5,1.0)-0.5
1.1102230246251565e-16                  # a relative epsilon value of 1.1e-16
>>> numpy.nextafter(500.0,501.0)-500.0
5.6843418860808015e-14                  # relative epsilon of 5.6e-14
>>> numpy.nextafter(1e17,1e18)-1e17
16.0                                    # the other end of the spectrum...

Так что вы можете видеть, что 1e-17 будет легко работать, чтобы увеличивать значения между 0,0 и 0,1, но не намного больше значений, чем это. Как вы можете видеть выше, относительное значение 1e17 равно 16.

+ 1 - отличный ответ. Посколькуmath.ldexp(x,i) такой же какx * (2**i) ваши примеры будут понятнее с2.0**-52 противmath.ldexp(1.0, -52) Просто говорю'.. the wolf
@ carrot-top: Thx - сделано user648852
3

большие" маленькие числа, такие как 1e-17, не так ли?

Не обязательно (это зависит от чисел). Afloat не может точно представлять либо1e-17 или1-(1e-17). В случае последнего, ближайшее число, которое этоможе представляет собой1.

Я предлагаю тебе прочитать Что каждый ученый должен знать об арифметике с плавающей точкой.

3

Если вам нужен такой уровень точности, рассмотрите Десятичный модуль

>>> decimal.Decimal(1.0)-decimal.Decimal('1.0e-17')
Decimal('0.999999999999999990')
>>> decimal.Decimal(1.0)-decimal.Decimal('1.0e-17')<decimal.Decimal(1.0)
True

А также

>>> decimal.Decimal(1.0)-decimal.Decimal('1.0e-17')<1.0
True

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

Другие предложили Что каждый учёный-компьютерщик должен знать об арифметике с плавающей точкой. и я тоже рекомендую Не храните это на поплавке

0

Вы можете справиться с этим. Обратите внимание, чт

>>> 1.e-17 == 0
False

а такж

>>> 1.e-17 + 1.e-18
1.1e-17

Вы просто не можете справиться с 1-1e-17, потому что мантисса не будет соответствовать конечной точности

0
>>> from decimal import Decimal
>>> Decimal('1')-Decimal(str(10**-17)) < Decimal('1')
True

десятичный модуль, для такой точности!

0

epsilon=2.220446049250313e-16 так что это нормально, что(1. - (1.e-17) ) = 1 потому что1.e-17 < epsilon.

Похожие вопросы