如何在创建 Numpy 数组时避免精度错误?
How to Avoid Precision Errors While Creating Numpy Arrays?
我的代码必须达到一定的精度。但是,我认为由于总结了长数组中的错误,它会累积更大的错误。你能找出为什么元素 38260 不等于 5.6 吗?
import numpy as np
xmin = -377
xmax = 345
spacing = 0.01
a = np.arange(xmin,xmax,spacing)
print('{0:.25f}'.format(a[38260]))
# Prints 5.5999999996520273271016777
print(a[38260] == 5.6)
# Prints false
b = np.arange(xmin/spacing,xmax/spacing)*spacing
print('{0:.25f}'.format(b[38260]))
#Prints 5.6000000000000005329070518
print(b[38260] == 5.6)
#Prints false
您可以通过创建一个 np.int32
开始,然后将其划分为您的浮点值范围来避免错误累积:
import numpy as np
xmin = -37700 # times 100
xmax = 34500 # times 100
spacing = 1 # times 100
b = np.arange(xmin, xmax, spacing) # np.int32 array - no loss of precision
a = b / 100.0 # divide by 100 # np.float64 array - closer to what you want
print('{0:.25f}'.format(a[38260]))
print(a[38260] == 5.6)
输出:
5.6
True
仍然建议使用 np.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False) 而不是 ==
进行相等比较。
您的原始代码已经在我的系统上使用了 np.float64
,所以我看不出有什么办法可以使它更精确,而且众所周知浮点数使用起来很灵活,请参阅:
- Is floating point math broken?
- What is the best way to compare floats for almost-equality in Python?.
要创建数组,您可以使用 list
,通过使用自定义 Floatrange
class,在 decimal
模块的帮助下:
from decimal import Decimal
class FloatRange:
def __init__(self, start, stop, step, include_right=False):
self.start = Decimal(f'{start}')
self.stop = Decimal(f'{stop}')
self.step = Decimal(f'{step}')
self.range = Decimal(f'{stop - start}')
self.include_right = include_right
self.len = len(self) + include_right
self.max_index = self.len - 1
self.count = 0
def __getitem__(self, index):
if index < 0:
index = self.len + index
if index > self.max_index:
raise IndexError('FloatRange index out of range.')
return float(self.start + index * self.step)
def __len__(self):
return int(self.range / self.step)
def __next__(self):
if self.count < self.len:
self.count += 1
return self[self.count-1]
if include_endpoint:
return stop
def __iter__(self):
while self.count < self.len:
yield next(self)
def to_numpy(self):
return np.fromiter(self)
def __repr__(self):
return (f"FloatRange("
f"start={self.start}, "
f"stop={self.stop}, "
f"step={self.step}, "
f"include_right={self.include_right})")
然后你可以创建一个FloatRange
对象:
>>> fr = FloatRange(xmin, xmax, spacing)
>>> fr
FloatRange(start=-377, stop=345, step=0.01, include_right=False)
>>> fr[-1]
344.99
>>> fr[38260]
5.6
>>> arr = fr.to_numpy() # Equivalent to: arr = np.array(list(fr))
>>> arr[38260]
5.6
如果include_right==True
:
>>> fr = FloatRange(xmin, xmax, spacing, include_right=True)
>>> fr[-1]
345.0
我的代码必须达到一定的精度。但是,我认为由于总结了长数组中的错误,它会累积更大的错误。你能找出为什么元素 38260 不等于 5.6 吗?
import numpy as np
xmin = -377
xmax = 345
spacing = 0.01
a = np.arange(xmin,xmax,spacing)
print('{0:.25f}'.format(a[38260]))
# Prints 5.5999999996520273271016777
print(a[38260] == 5.6)
# Prints false
b = np.arange(xmin/spacing,xmax/spacing)*spacing
print('{0:.25f}'.format(b[38260]))
#Prints 5.6000000000000005329070518
print(b[38260] == 5.6)
#Prints false
您可以通过创建一个 np.int32
开始,然后将其划分为您的浮点值范围来避免错误累积:
import numpy as np
xmin = -37700 # times 100
xmax = 34500 # times 100
spacing = 1 # times 100
b = np.arange(xmin, xmax, spacing) # np.int32 array - no loss of precision
a = b / 100.0 # divide by 100 # np.float64 array - closer to what you want
print('{0:.25f}'.format(a[38260]))
print(a[38260] == 5.6)
输出:
5.6
True
仍然建议使用 np.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False) 而不是 ==
进行相等比较。
您的原始代码已经在我的系统上使用了 np.float64
,所以我看不出有什么办法可以使它更精确,而且众所周知浮点数使用起来很灵活,请参阅:
- Is floating point math broken?
- What is the best way to compare floats for almost-equality in Python?.
要创建数组,您可以使用 list
,通过使用自定义 Floatrange
class,在 decimal
模块的帮助下:
from decimal import Decimal
class FloatRange:
def __init__(self, start, stop, step, include_right=False):
self.start = Decimal(f'{start}')
self.stop = Decimal(f'{stop}')
self.step = Decimal(f'{step}')
self.range = Decimal(f'{stop - start}')
self.include_right = include_right
self.len = len(self) + include_right
self.max_index = self.len - 1
self.count = 0
def __getitem__(self, index):
if index < 0:
index = self.len + index
if index > self.max_index:
raise IndexError('FloatRange index out of range.')
return float(self.start + index * self.step)
def __len__(self):
return int(self.range / self.step)
def __next__(self):
if self.count < self.len:
self.count += 1
return self[self.count-1]
if include_endpoint:
return stop
def __iter__(self):
while self.count < self.len:
yield next(self)
def to_numpy(self):
return np.fromiter(self)
def __repr__(self):
return (f"FloatRange("
f"start={self.start}, "
f"stop={self.stop}, "
f"step={self.step}, "
f"include_right={self.include_right})")
然后你可以创建一个FloatRange
对象:
>>> fr = FloatRange(xmin, xmax, spacing)
>>> fr
FloatRange(start=-377, stop=345, step=0.01, include_right=False)
>>> fr[-1]
344.99
>>> fr[38260]
5.6
>>> arr = fr.to_numpy() # Equivalent to: arr = np.array(list(fr))
>>> arr[38260]
5.6
如果include_right==True
:
>>> fr = FloatRange(xmin, xmax, spacing, include_right=True)
>>> fr[-1]
345.0