1

why this two call to toBinary function compute the same output (at least under VS2010) ?

#include <iostream>
#include <bitset>
#include <limits>
using namespace std;

template<class T> bitset<sizeof(T)*CHAR_BIT> toBinary(const T num) 
{
    bitset<sizeof(T)*CHAR_BIT> mybits;
    const char * const p = reinterpret_cast<const char*>(&num);
    for (int i = sizeof(T)*CHAR_BIT-1 ; i >= 0 ; --i)
        mybits.set(i, (*(p)&(1<<i) ));
    return mybits;
}

int main() 
{
    cout << toBinary(8.9).to_string() << "\n"; 
    cout << toBinary( 8.9 + std::numeric_limits<double>::epsilon() ).to_string()  << "\n"; 
    cin.get();
}
Guillaume Paris
  • 10,303
  • 14
  • 70
  • 145

2 Answers2

5

That epsilon is relative to 1; here, instead, you are summing it to 8.9, which is more than 8 (2^3) times bigger than 1. This means that that epsilon would change a binary digit that is three digits to the right of the rightest digit stored in that double.

If you want to notice something change, you have to add at about 8.9*epsilon.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • which value should I add to get the smallest increment, because if I add 8.8 for instance, I also get a change? – Guillaume Paris Apr 14 '11 at 22:11
  • There is no single value that does what you want, but there is a library function - see my answer. – zwol Apr 14 '11 at 22:14
  • Hmmm, I too tested it and got that result, I think it depends on the exact internal representation of the number. A good rule of thumb is that `number*epsilon` gets you a value that, added to `number`, will *certainly* change it, while under it you don't have guarantees. – Matteo Italia Apr 14 '11 at 22:15
3

You have two problems. The first is that your toBinary function doesn't do what you wanted -- it should read like so (assuming a little-endian CPU):

template<class T> bitset<sizeof(T)*CHAR_BIT> toBinary(const T num)
{
    bitset<sizeof(T)*CHAR_BIT> mybits;
    const char * const p = reinterpret_cast<const char*>(&num);
    for (int i = sizeof(T)-1; i >= 0; i--)
        for (int j = CHAR_BIT-1; j >= 0; j--)
            mybits.set(i*CHAR_BIT + j, p[i] & (1 << j));
    return mybits;
}

The other problem is as Matteo describes: numeric_limits<double>::epsilon is the difference between 1.0 and the next larger representable value, not the difference between any floating point number and the next larger representable value. You can see this for yourself by modifying your program to try incrementing 0.5, 1.0, and 2.0 -- adding epsilon will increment the second-to-last bit of 0.5, the last bit of 1.0, and have no effect on 2.0.

There is a way to do what you're trying to do, though: the nextafter family of functions (they're part of C99).

zwol
  • 135,547
  • 38
  • 252
  • 361
  • i don't get why ma function has a error.According to me (*(p)&(1< – Guillaume Paris Apr 15 '11 at 22:35
  • No, it wont. `*p` has type `char`, so it retrieves either the first or the last `CHAR_BIT` bits of your `double` (depending on endianness), and *only* those bits. So most of your loop is not reading bit `i` of the `double`, but a sign-extension copy of bit 7. To see the second and subsequent bytes of the `double`, you have to offset `p` *before* dereferencing it, which is what my modified loop does. – zwol Apr 15 '11 at 22:58
  • I don't get why it's (1< – Guillaume Paris Apr 16 '11 at 10:04
  • Because `i` is the *byte* index. Each loop iteration needs to access the bit at `i*CHAR_BIT + j` within the original value. If it used `1< – zwol Apr 16 '11 at 15:01
  • what is it not natural for me it is that for two differents bytes index (i=0 and i=8 for instance) we have the same mask (j=0)...so the mask doesn't match the correct position for i =0, at least in my mind... – Guillaume Paris Apr 16 '11 at 17:37
  • You're still thinking about this as if we were operating on a giant bitfield containing the entire double. We're not. `p[i]` extracts a *single byte* from the double, at offset `i`; `& (1 << j)` extracts a bit *from that byte*, at offset `j`. The bit numbers within each byte are always 0..(CHAR_BIT-1). – zwol Apr 16 '11 at 19:08
  • There is, for the record, no portable way to get a "giant bitfield containing the entire double". This is how you have to do it, unless you like nasty tricks with unions. – zwol Apr 16 '11 at 19:09