допустимый элемент, которого нет в np.arange, хотя print() показывает его, dtypes также одинаковы

#python #numpy

#python #numpy

Вопрос:

 a = 1.25
if a in np.arange(1,2,0.05):
    print(a)
  

но это условие if не выполняется, хотя

 print(np.arange(1,2,0.05))
[ 1.   1.05  1.1   1.15  1.2   1.25  1.3   1.35  1.4   1.45  1.5   1.55   1.6   1.65  1.7   1.75  1.8   1.85  1.9   1.95]
  

в нем есть 1.25.

Я читал, что arange на самом деле не является последовательным, но есть ли какой-либо обходной путь для этого?

Ответ №1:

Это потому 1.25 , что в этом диапазоне его на самом деле нет 1.2500000000000002 , и это потому, что numpy по умолчанию использует значение с плавающей запятой двойной точности для чисел с плавающей запятой, а в случаях, когда десятичные дроби не могут быть представлены точно как двоичные дроби, они могут содержать более точное значение для чисел с плавающей запятой. И то, что вы видите в результате печати, является просто форматированным представлением реального значения.

 In [58]: l = np.arange(1,2,0.05)

In [59]: l[5]
Out[59]: 1.2500000000000002

In [60]: type(l[5])
Out[60]: numpy.float64
  

Одним из способов решения этой проблемы является приведение типа к float32 :

 In [61]: l = np.arange(1,2,0.05).astype(np.float32)

In [62]: a = 1.25

In [63]: type(a)
Out[63]: float

In [64]: a in l
Out[64]: True
  

Комментарии:

1. «десятичные дроби не могут быть представлены точно как двоичные дроби» — это в равной степени верно для литерала 1.25 , с которым сравниваются значения в массиве (который также будет 64-битным с плавающей точкой). Я думаю, проблема в том, что размер шага не может быть представлен точно как двоичная дробь. Например, 1.25 in np.arange(1, 2, 2**-4) == True .

2. @ali_m Да, спасибо за разъяснение, но, как я уже сказал, это decimal fractions , и это происходит после добавления шага, и причиной может быть шаг или результат.

Ответ №2:

Используется item для печати значений в полном великолепии:

 In [83]: for i in np.arange(1,2,0.05):print(i.item())
1.0
1.05
1.1
1.1500000000000001
1.2000000000000002
1.2500000000000002
1.3000000000000003
1.3500000000000003
1.4000000000000004
1.4500000000000004
1.5000000000000004
1.5500000000000005
1.6000000000000005
1.6500000000000006
1.7000000000000006
1.7500000000000007
1.8000000000000007
1.8500000000000008
1.9000000000000008
1.9500000000000008
  

Даже linspace эти дополнительные цифры отключены в конце:

 In [84]: for i in np.linspace(1,2,21):print(i.item())
1.0
1.05
1.1
1.15
1.2
1.25
1.3
1.35
1.4
1.45
1.5
1.55
1.6
1.65
1.7000000000000002
1.75
1.8
1.85
1.9
1.9500000000000002
2.0
  

in и == тесты не являются хорошей идеей при работе с поплавками.

isclose и allclose проверка на небольшие различия, а не на точное совпадение

 In [89]: np.isclose(np.arange(1,2,0.05),1.25)
Out[89]: 
array([False, False, False, False, False,  True, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False], dtype=bool)
In [90]: np.isclose(np.arange(1,2,0.05),1.25).any()
Out[90]: True
  

Посмотрите на isclose код, чтобы увидеть все, что связано с сравнением чисел с плавающей точкой.