Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I write const expression double that is two ulps less than -0.5

I have a set of floating point calculation based on numbers I receive via a json packet. At the end of my calculation I require one of the numbers to be >= -0.5. I'm finding that sometimes I have a value that fails the test because it is one ULP below the threshold. Is there anyway to write a constexpression that means something like

constexpr auto threshold = -0.5 - 2*ULP;

or do I have to resort to something like

auto threshold = -0.5;
threshold = std::nexttoward(threshold, -2.0);
threshold = std::nexttoward(threshold, -2.0);
like image 571
John Gordon Avatar asked Aug 08 '17 12:08

John Gordon


2 Answers

You should be able to achieve desired threshold with epsilon, something like

constexpr auto threshold = -0.5 - std::numeric_limits<double>::epsilon();

Possibly add *2 if you think you really need it, though since epsilon is defined for value 1.0, it might work out just right for you here for 0.5.


Alternatively, just don't use it as constexpr. Unless it is some inner loop in some very performance sensitive code, the difference should be negligible:

const auto threshold = std::nexttoward(std::nexttoward( -0.5, -2.0), -2.0);
like image 100
hyde Avatar answered Sep 30 '22 04:09

hyde


Maybe something like this can do it (it needs base 2 floating point representation, and it doesn't work for denormals):

constexpr double num = -0.5;
constexpr double threshold = num + 
          2.0 
        * (num < 0 ? -1 : 1) 
        * std::pow(2.0,
                   std::floor(std::log(std::abs(num)) / std::log(2.0))) 
        * std::numeric_limits<double>::epsilon();

How does it work (I describe it with IEEE754 in mind)?

Epsilon means 1 ULP when a number is in the range of [1.0;2.0). We need to scale epsilon, so it always means 1 ULP. The scale is based on the exponent part of the floating point number. If the number is [1.0;2.0), then the scale must be 1. If the number is [2.0;4.0), then the scale must be 2, for [4.0;8.0), it must be 4, etc. So, we need to find the nearest, less-than-or-equal power of 2: it is 2^floor(log2(number)). And we need to care of negative numbers, that's why the abs and (num<0?-1:1) in the formula.

like image 32
geza Avatar answered Sep 30 '22 02:09

geza