Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine the minimal noticable change of a double

I have the problem to determine the smallest value eps for a given double variable v such that

v+eps != v

Note that this is not the typical problem sheet task since eps depends on the arbitrary number v.

This should not be done by seeking this value in a for loop. Is there a fast way to do this, e.g. by bit shifting? Independent of the compiler, optimization flags, platform...

Thanks for your answers

like image 802
choosyg Avatar asked Oct 10 '13 09:10

choosyg


People also ask

How do you measure just noticeable difference?

An experimenter slowly adds tiny amounts of sand to one hand and asks you to say when you notice that one hand feels heavier than the other. The smallest weight difference that you can detect at least half the time is the just noticeable difference.

What is the smallest detectable difference between two stimuli?

Just noticeable difference (JND): The smallest detectable difference between two stimuli, or the minimum change in a stimulus that can be correctly judged as different from a reference stimulus; also known as difference threshold.

What does Weber's law suggest about the just noticeable difference?

Weber's Law states that the concept that a just-noticeable difference in a stimulus is proportional to the magnitude of the original stimulus.

What is the minimal difference needed to notice a stimulus change?

A difference threshold is the minimum required difference between two stimuli for a person to notice change 50% of the time (and you already know where that “50% of the time” came from). The difference threshold is also called just noticeable difference, which translates the concept more clearly.


2 Answers

The C99 function nextafter is what you need. Alternatively, use Boost.Math's nextafter. This is implementation defined by definition (it relies on the internal representation of double in memory).

For a comparison of all methods presented in the answers here at the time of writing, see a live demo to see how the other solutions fail.


For reference, here is the test code if you want to run it on our own system:

#include <cmath>
#include <cfloat>
#include <limits>
#include <iostream>
using std::cout;
#include <iomanip>
using std::setprecision;

#include <boost/math/special_functions/next.hpp>

double
epsFor( double x )
{
  union
  {
    double d;
    unsigned long long i;
  } tmp;
  tmp.d = x;
  ++ tmp.i;
  return tmp.d - x;
}

void test(double d)
{
  double d1 = std::nextafter(d,DBL_MAX);
  double d2 = d+std::numeric_limits<double>::epsilon() * d;
  double d3 = d+epsFor(d);
  double d4 = boost::math::nextafter(d, DBL_MAX);
  cout << setprecision(40)
       << "For value of d = " << d << '\n'
       << " std::nextafter: " << d1 << '\n'
       << " Boost solution: " << d4 << '\n'
       << " undefined beh.: " << d3 << '\n'
       << " numeric_limits: " << d2 << '\n';
}

int main()
{
  test(0.1);
  test(986546357654.354687);
}
like image 125
rubenvb Avatar answered Sep 27 '22 18:09

rubenvb


I'd use type punning:

double
epsFor( double x )
{
    union
    {
        double d;
        unsigned long long i;
    } tmp;
    tmp.d = x;
    ++ tmp.i;
    double results = tmp.d - x;
    return results;
}

(Formally, this is undefined behavior, but in practice, I don't know of a modern compiler where it will fail.)

EDIT:

Note that C++ allows excessive precision in intermediate expressions; since we're concerned here with exact results, the originally posted function could give wrong results if you used it directly in an expression, rather than assigning it to a double. I've added an assignment in the function to avoid this, but be aware that a lot of compilers are not standard conform in this regard, at least by default. (g++ is a good example of one where you need a special option to have conformant behavior, at least when optimization is turned on. If you're using g++, you must specify the -ffloat-store option if you want correct results.)

like image 30
James Kanze Avatar answered Sep 27 '22 17:09

James Kanze