152

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
  • 10,427
  • 24
  • 63
  • 71
  • 13
    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

274

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
  • 71,714
  • 23
  • 223
  • 298
  • 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
  • 6
    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
  • 4
    @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
119

Another easy way to get epsilon is:

In [1]: 7./3 - 4./3 -1
Out[1]: 2.220446049250313e-16
ali_m
  • 71,714
  • 23
  • 223
  • 298
Ullen
  • 1,255
  • 1
  • 8
  • 2
  • 9
    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
  • 30
    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
  • 17
    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
  • 55
    This answer does not require any knowledge of Python or numpy internals. – GuillaumeDufay Oct 12 '17 at 22:16
  • 4
    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
  • 13
    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
  • 5
    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
  • @Gulzar disagree. It's a good answer precisely because it avoids usage of a totally unneeded package (numpy) and relies on the precise definition of machine epsilon. Whether it makes the codebase worse depends on how one wraps and names this function and how well-documented it is. – C S Aug 08 '23 at 22:47
17

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
  • 224,032
  • 165
  • 485
  • 680
  • 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