2

I have to get certain precision in my code. However, I think due to summing up of errors in long arrays, it accumulates on a bigger error. Would you identify why element 38260 is not equal to 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
user3243944
  • 103
  • 7

2 Answers2

3

You can avoid the error accumulation by creating an np.int32 to begin with, then dividing it into your float-value range:

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)

Output:

 5.6
 True

It is still adviseable to use np.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False) instead of == for equality-comparison.

Your original code already uses np.float64 on my system so I see no way to make that more precise and floats are known to be fickly to work with, see:

Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
1

To create the array you can use list, by using a custom Floatrange class, with the help of decimal module:

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})")

Then you can create a FloatRange object:

>>> 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

If include_right==True:

>>> fr = FloatRange(xmin, xmax, spacing, include_right=True)
>>> fr[-1]
345.0
Sayandip Dutta
  • 15,602
  • 4
  • 23
  • 52