Python: добавление элемента с плавающей точкой к массиву изменяет его точность, например, с 1.2 до 1.200000002
Я столкнулся с очень странной проблемой. Я пытаюсь создать функцию, которая возвращает массив значений, которые заключают диапазон с определенным размером шага (например, вы можете найти на оси графика). Вместо того, чтобы просто использовать np.arange(min,max,step)
Я хочу что-то, что лучше округляет размер шага. Вот что я попробовал:
def get_decade(value):
return pow(10,math.floor(math.log10(value)))
def get_steparray(min,max,delta):
delta_step = float(get_decade(delta))
next = math.floor(min/delta_step)*delta_step
print next
array = [next]
while next < max:
next = int((next+delta_step)/delta_step)*delta_step
print next
array.append(next)
print array
print array
return array
Там есть печатные заявления, которые помогут мне понять, что происходит. Вот что я попытался запустить с:
print get_steparray(1.032,1.431,0.1)
Исходя из этого, я ожидал, что массив в конечном итоге [1.0,1.1,1.2,1.3,1.4,1.5]
Вот что я получаю от функции:
1.0
1.1
[1.0, 1.1]
1.2
[1.0, 1.1, 1.2000000000000002]
1.3
[1.0, 1.1, 1.2000000000000002, 1.3]
1.4
[1.0, 1.1, 1.2000000000000002, 1.3, 1.4000000000000001]
1.5
[1.0, 1.1, 1.2000000000000002, 1.3, 1.4000000000000001, 1.5]
[1.0, 1.1, 1.2000000000000002, 1.3, 1.4000000000000001, 1.5]
Как вы можете видеть, некоторые из них работают, а другие добавляют дополнительные десятичные дроби.
У кого-нибудь есть идеи, что может быть причиной этого? Спасибо за любую информацию, которую вы можете предоставить. С другой стороны, я был бы так же рад, если бы кто-то знал лучший, более функциональный способ создания такого массива. Может быть, я должен просто придерживаться np.arange и корректировать значения max/min/step?
(Да, я знаю, что мой код не самый чистый. Приведенная выше функция начиналась намного чище, но я добавил некоторые ненужные функции, чтобы попытаться заставить его работать.)
Изменить: Хотя я ценю все проницательные комментарии, я все еще не уверен, что они решают мою проблему полностью. Как видно на распечатке, каждое значение хранится с достаточной точностью, как мои потребности, в качестве изолированного типа с плавающей запятой. Но когда они добавляются в массив, только тогда они меняют точность. Я в целом хорошо осведомлен о проблемах с плавающей запятой, но мне было любопытно узнать о конкретных различиях между плавающей точкой и массивом. Интересно, может быть, массив хранит значение в меньшем количестве бит, чем отдельное значение.
При этом, я думаю, что в конечном итоге я сделаю предложение сосредоточиться на форматировании в момент использования.
Спасибо!
1 ответ
Зачем?
Это типичная арифметическая проблема с плавающей точкой.
Я опубликую здесь выдержку из документации Python для арифметических проблем и ограничений с плавающей точкой, но это верно для большинства языков:
Числа с плавающей точкой представлены в компьютерном оборудовании в виде двоичных (двоичных) дробей. Например, десятичная дробь
0.125
имеет значение
1/10 + 2/100 + 5/1000
и таким же образом двоичная дробь0.001
имеет значение
0/2 + 0/4 + 1/8
, Эти две дроби имеют одинаковые значения, единственное реальное отличие состоит в том, что первая записана в дробной нотации 10, а вторая - в 2.К сожалению, большинство десятичных дробей не могут быть представлены точно как двоичные дроби. Следствием этого является то, что, как правило, вводимые десятичные числа с плавающей запятой аппроксимируются только двоичными числами с плавающей запятой, фактически сохраненными в машине.
Проблема легче понять сначала в базе 10. Рассмотрим дробь
1/3
, Вы можете приблизить это как основную 10 фракцию:0.3
или лучше,
0.33
или лучше,
0.333
и так далее. Независимо от того, сколько цифр вы хотите записать, результат никогда не будет точно
1/3
, но будет все более лучшим приближением1/3
,
Как?
Итак, теперь, когда вы знаете, почему, вот как вы можете обойти это.
Если вам не нужна вся эта точность (или в этом вопросе... вся эта неточность), вы всегда можете:
- использовать
round
встроенная - оставьте номер как есть, но отформатируйте его только тогда, когда вам нужно, чтобы он был напечатан для графического интерфейса или консоли