134

I am trying to understand what is machine epsilon. According to the Wikipedia, it can be calculated as follows:

def machineEpsilon(func=float):
    machine_epsilon = func(1)
    while func(1)+func(machine_epsilon) != func(1):
        machine_epsilon_last = machine_epsilon
        machine_epsilon = func(machine_epsilon) / func(2)
    return machine_epsilon_last

However, it is suitable only for double precision numbers. I am interested in modifying it to support also single precision numbers. I read that numpy can be used, particularly numpy.float32 class. Can anybody help with modifying the function?

Bob
  • 9,583
  • 22
  • 60
  • 69
  • 10
    That function is general enough to work with all precisions. Just pass a `numpy.float32` as an argument to the function! – David Zwicker Oct 02 '13 at 16:08

3 Answers3

242

An easier way to get the machine epsilon for a given float type is to use np.finfo():

print(np.finfo(float).eps)
# 2.22044604925e-16

print(np.finfo(np.float32).eps)
# 1.19209e-07
ali_m
  • 67,619
  • 20
  • 215
  • 288
  • 3
    just to be 100% confident, the first one provides python "standard" precision of innate floats while the second one the precision of numpy's floats? – Charlie Parker Nov 01 '17 at 16:47
  • 3
    note that numpy's standard accuracy is 64 (in a 64 bit computer): `>>> print(np.finfo(np.float).eps) = 2.22044604925e-16` and `>>> print(np.finfo(np.float64).eps) = 2.22044604925e-16` – Charlie Parker Nov 01 '17 at 17:24
  • 3
    @CharlieParker I could have used `np.float` instead, since it's just an alias of Python's builtin `float`. Python floats are 64-bit (C `double`) on almost all platforms. `float` and `np.float64` therefore usually have equivalent precision, and for most purposes you can use them interchangeably. However they aren't identical - `np.float64` is a numpy-specific type, and an `np.float64` scalar has different methods to a native `float` scalar. As you'd expect, `np.float32` is a 32-bit float. – ali_m Nov 01 '17 at 18:46
109

Another easy way to get epsilon is:

In [1]: 7./3 - 4./3 -1
Out[1]: 2.220446049250313e-16
ali_m
  • 67,619
  • 20
  • 215
  • 288
Ullen
  • 1,131
  • 1
  • 7
  • 2
  • 6
    Yeah, and why does `8./3 - 5./3 - 1` yield `-eps`, and `4./3 - 1./3 - 1` yields zero, and `10./3 - 7./3 - 1` yields zero? – Steve Tjoa Jul 08 '15 at 18:20
  • 22
    Ah, the answer is here, Problem 3: http://rstudio-pubs-static.s3.amazonaws.com/13303_daf1916bee714161ac78d3318de808a9.html Basically, if you subtract the binary representation of 4/3 from 7/3, you get the definition of machine epsilon. So I suppose this should hold for any platform. – Steve Tjoa Jul 08 '15 at 18:24
  • 15
    This is too esoteric of an answer which requires too much knowledge of Python and `numpy` internals when there's an existing `numpy` function to find the epsilon. – Olga Botvinnik Oct 26 '15 at 20:14
  • 41
    This answer does not require any knowledge of Python or numpy internals. – GuillaumeDufay Oct 12 '17 at 22:16
  • 3
    That would need to be `abs(7./3 - 4./3 - 1)`, because otherwise one gets `-1.192093e-07` on an [ESP32 microcontroller](https://en.wikipedia.org/wiki/ESP32). – Serge Stroobandt Dec 25 '17 at 13:25
  • 9
    Indeed, it asserts that the reader is aware about Python running on computers that aren't using underlying base-3 computation. – kokociel Jul 24 '18 at 08:02
  • 1
    The only reason this is upvoted is because it's cool. But it is a bad answer. I hope no code base has to deal with something looking like this. – Gulzar May 13 '21 at 12:15
16

It will already work, as David pointed out!

>>> def machineEpsilon(func=float):
...     machine_epsilon = func(1)
...     while func(1)+func(machine_epsilon) != func(1):
...         machine_epsilon_last = machine_epsilon
...         machine_epsilon = func(machine_epsilon) / func(2)
...     return machine_epsilon_last
... 
>>> machineEpsilon(float)
2.220446049250313e-16
>>> import numpy
>>> machineEpsilon(numpy.float64)
2.2204460492503131e-16
>>> machineEpsilon(numpy.float32)
1.1920929e-07
Claudiu
  • 216,039
  • 159
  • 467
  • 667
  • 1
    btw your function will raise `NameError` if condition in `while` will be satisfied on the first check, so it probably makes sense to do `machine_epsilon = machine_epsilon_last = func(1)` in the first statement – Azat Ibrakov Sep 22 '19 at 10:13