3

What are the smallest positive values in C++ for which their multiplicative inverse will still be finite? Have tried numeric_limits<double>::epsilon() but it isn't it - am getting positive values for much smaller values than that.

#include <limits>//is it here?
void tfuuuuu()
{

   double d_eps,invd_eps;
   float f_eps,invf_eps;
   invd_eps = 1.0/d_eps;//invd_eps should be finite
   invf_eps = 1.f/f_eps;//invf_eps should be finite

}
jamesdlin
  • 81,374
  • 13
  • 159
  • 204
Benny K
  • 1,957
  • 18
  • 33
  • Have you even checked what is in header? – eozd Nov 24 '18 at 20:22
  • @eozd - Yes, the only possible value which I see is numeric_limits::epsilon(), but it is definitely not what I am looking for. – Benny K Nov 24 '18 at 20:25
  • If so, please add this detail in your question, as well. It is important to demonstrate what you researched so far so that you get proper answers. – eozd Nov 24 '18 at 20:26
  • Do you mean `std::numeric_limits::min()` and `1.f/std::numeric_limits::min()`? – t.niese Nov 24 '18 at 20:31
  • @t.niese: No, `min()` would be a very high-absolute-value negative number. – einpoklum Nov 24 '18 at 20:32
  • Why don't you write a small program and check? Use a [bisection](https://en.wikipedia.org/wiki/Bisection_method)-like approach. – einpoklum Nov 24 '18 at 20:34
  • 4
    According to the IEEE 754 standard, a single precision floating is encoded on 32 bits with a sign bit, a fraction and an exponent. The smallest positive value you can have is 2^(-126). (and it is also possible to represent its inverse 2^126 ) – sowlosc Nov 24 '18 at 20:35
  • "in C++" is the wrong question anyway. IEEE 754? – Swordfish Nov 24 '18 at 20:35
  • 1
    @einpoklum - have tried that, but I assume that it won't be generic (i.e., platform specific, compiler specific, etc.) so I am looking for something generic, maybe part of the language standard. – Benny K Nov 24 '18 at 20:37
  • @Swordfish - was wondering if there is something standard in the header for example like epsilon() which is also part of the IEEE standard – Benny K Nov 24 '18 at 20:41
  • [Fundamental types (§6.7.1/11)](http://eel.is/c++draft/basic.fundamental#11) *The value representation of floating-point types is implementation-defined.* – Swordfish Nov 24 '18 at 20:42
  • The question is: Does the underlying C++ architecture use IEEE 754 for `double` number representation? Maybe `std::nextafter(0.0, 1.0)` – KamilCuk Nov 24 '18 at 20:43
  • 1
    `std::numeric_limits<>::epsilon()` gives the difference between `1.0` and the next representable value. For floating point types with denormalisation `std::numeric_limits<>::min()` produces the smallest *normalised* value, not (necessarily) the smallest of all positive values. The C++17 header `` has a macro value `DBL_TRUE_MIN` for `double` (and similar macros for other floating point types) but this is not yet (?) reflected in ``. Without these macros, technique to calculate it depend on the floating point representation..... – Peter Nov 24 '18 at 21:58

2 Answers2

3

I doubt there is a standard library function for finding the number you need, however, it is not too hard to find the needed value using a simple binary search:

#include <iostream>
#include <cmath>
#include <typeinfo>

template<typename T> T find_magic_epsilon(T from, T to) {
    if (to == std::nextafter(from, to)) return to;
    T mid = (from + to)/2;
    if (std::isinf((T)1.0/mid)) return find_magic_epsilon<T>(mid, to);
    else return find_magic_epsilon<T>(from, mid);
}

template<typename T> T meps() {
    return find_magic_epsilon<T>(0.0, 0.1);
}

template<typename T> T test_meps() {
    T value = meps<T>();
    std::cout << typeid(T).name() << ": MEPS=" << value
              << " 1/MEPS=" << (T)1.0/value << " 1/(MEPS--)="
              << (T)1.0/std::nextafter(value,(T)0.0) << std::endl;
}

int main() {
    test_meps<float>();
    test_meps<double>();
    test_meps<long double>();
    return 0;
}

The output of the script above:

f: MEPS=2.93874e-39 1/MEPS=3.40282e+38 1/(MEPS--)=inf
d: MEPS=5.56268e-309 1/MEPS=1.79769e+308 1/(MEPS--)=inf
e: MEPS=8.40526e-4933 1/MEPS=1.18973e+4932 1/(MEPS--)=inf
KT.
  • 10,815
  • 4
  • 47
  • 71
  • You divide by zero and get away with it? O.O – Swordfish Nov 24 '18 at 22:11
  • Division by zero is a normal operation for IEEE floats, producing an "infinity" value as a result. Moreover, you can divide by negative zero (a separate value), you'll get negative infinity - fun, right? However, if you feel uneasy about this practice (a totally valid concern), you should use numeric_limits::infinity() instead. – KT. Nov 24 '18 at 22:14
  • In fact, std::isinf is probably a nicer way to check for infinity anyway. Fixed the code there. – KT. Nov 24 '18 at 22:20
  • I didn't dare run your code after I saw that. And I certainly won't! – Swordfish Nov 24 '18 at 22:22
  • That's why I included the output in the answer - for those who do not dare! ;) – KT. Nov 24 '18 at 22:29
2

Here is a starting point to answer your question. I have simply divided by 2. Once you get to dividing by 2^^148, you come pretty close. You could then iteratively move closer and print out the hex representation of the number to see what the compiler is doing:

#include <iostream>
#include <stdio.h>


#include <limits>//is it here?

int main() {
    double d_eps,invd_eps;
    float f_eps,invf_eps;
    invf_eps = 1.f/f_eps;//invf_eps should be finite
    float last_seed = 0;
    float seed = 1.0;
    for(int i = 0; i < 1000000; i++) {
        last_seed = seed;
        seed = seed/2;
        if(seed/2 == invd_eps) {
            printf("Breaking at i = %d\n", i);
            printf("Seed:  %g, last seed: %g\n", seed, last_seed);
            break;
        }
    }

    printf("%f, %lf, %f, %lf\n\n", f_eps, d_eps, invf_eps, invd_eps);
    return 0;
}

Output:

Breaking at i = 148
Seed:  1.4013e-45, last seed: 2.8026e-45
0.000000, 0.000000, inf, 0.000000


Process finished with exit code 0
Gardener
  • 2,591
  • 1
  • 13
  • 22