4

I am trying to change one bit in a double so that for example:

Double x: -1.500912597 which is:

Binary: 10111111 11111000 00000011 10111100 11101101 01100100 01001111 10010011

Change one bit in the underlying binary code (for example, bit 16) so that:

Binary: 10111111 11111001 00000011 10111100 11101101 01100100 01001111 10010011

Double x: -1.563412596999999903

Is there some C++ code I can use for this?

Milan
  • 205
  • 2
  • 9
  • Yes, there is some C++ code that can be used for this. Your Google keyword is "union". – Sam Varshavchik May 10 '16 at 11:04
  • Learn to use [bitwise operations in C/C++](http://www.cprogramming.com/tutorial/bitwise_operators.html) – Jens May 10 '16 at 11:05
  • 3
    @SamVarshavchik Not in standard C++ – M.M May 10 '16 at 11:06
  • 1
    @Jens bitwise operators cannot be used on `double` – M.M May 10 '16 at 11:07
  • 3
    @JoachimPileborg You can write the bit by using an `unsigned char *` (or `memcpy`) which does not violate strict aliasing... the issue is that the system might use various byte orderings (that's if we assume that ieee754 is in use) – M.M May 10 '16 at 11:10
  • 1
    It's possible but you need to bypass the [strict aliasing rule](http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule) by treating your variable as an array of `unsigned char` using pointers. That have other problems though, relating to byte ordering and the actual floating-point format used (as commented by @M.M). And that will also make the code hard to understand and maintain, which is more important (IMO). – Some programmer dude May 10 '16 at 11:17
  • @M.M which is why Sam suggests to use a `union`. – Jens May 10 '16 at 11:25
  • @Jens Type punning using unions is allowed in C, but not in C++. – Some programmer dude May 10 '16 at 11:33
  • @JoachimPileborg: IMO, flipping a bit in a `double` value is as bad as using a `union` which which is as hacky as `l = *((uint64_t *)&x)`. But that's not the question here. – Jens May 10 '16 at 12:14

3 Answers3

2

The only portable way is to use memcpy (yes, I know what you're thinking, and no it's not inefficient).

Note that this solution does not take into account byte-ordering. You'd need to cater for that too to be strictly portable.

#include <cstdlib>
#include <cstring>
#include <utility>
#include <iostream>

// only compile this function if Integer is the same size as a double
template<class Integer, std::enable_if_t<sizeof(Integer) == sizeof(double)>* = nullptr>
double or_bits(double input, Integer bits)
{
  Integer copy;

  // convert the double to a bit representation in the integer 
  std::memcpy(&copy, &input, sizeof(input));
  // perform the bitwise op
  copy |= bits;
  // convert the bits back to a double
  std::memcpy(&input, &copy, sizeof(copy));
  // return the double
  return input;
}

int main()
{
  double d = 1.0;
  d = or_bits(d, 0x10ul);
  std::cout << d << std::endl;
}

assembly output on gcc5.3:

double or_bits<unsigned long, (void*)0>(double, unsigned long):
    movq    %xmm0, %rax
    orq     %rdi, %rax
    movq    %rax, -8(%rsp)
    movsd   -8(%rsp), %xmm0
    ret
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • As far as I know, `memcpy` is not the only portable way. Manually changing bits using `char *` is well-defined too. – HolyBlackCat May 10 '16 at 11:35
  • I literally copied and pasted your code (into VS2015) but I get a few errors: http://pastebin.com/YhkSQzq2. Is this because I'm using a different compiler then? – Milan May 10 '16 at 13:31
  • 1
    @Milan i think it's because `unsigned long` on that system is not the same size as `double`. You'll need to deduce the right type from the size. – Richard Hodges May 10 '16 at 13:33
  • @RichardHodges Indeed, unsigned long = 4 bytes while double = 8 bytes on my system, so I guess it won't compile the function then. – Milan May 10 '16 at 13:59
  • 1
    @milan change the 0x10ul to 0x10ull – Richard Hodges May 10 '16 at 14:05
  • @RichardHodges Now it runs indeed! When I set `d = 1.0` and set `d = or_bits(d, 0x1ull);` I would expect d to turn into -1.0 since the first bit in a double is the bit that decides whether it's a positive or negative number and that one should flip, I think? d however stays at 1.. Sorry for all the questions, but I don't see how it actually flips the first bit then. Is this because of the byte ordering you mentioned before? – Milan May 10 '16 at 14:28
  • @Milan you may well now have to conditionally check for byte ordering and reverse the argument on a little-endian system. – Richard Hodges May 10 '16 at 14:32
  • @RichardHodges Turns out I'm using a little endian system indeed. Hopefully my last question, but what exactly do you mean by reverse the argument? I've tried to reverse a few parts of the code now (yeah..) but can't get it to work. Not sure if my brain is fried by now or it's just because I've never used memcpy before, but I really just can't figure it out :( – Milan May 10 '16 at 14:43
1
#include <ieee754.h>

ieee754_double d = {-1.500912597};
d.ieee.mantissa1 |= 1u << 16; // set bit 16 of mantissa
double x = d.d;

Header ieee754.h on my system comes from glibc-headers package.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • 2
    `ieee754.h` is not part of ISO C++ , it would be good to add to your answer explaining what packages etc. are required to use your code – M.M May 10 '16 at 11:09
  • Even though ieee754.h is not part of ISO C++, I think accessing this way is cleaner than the alternatives. – Erik Alapää May 10 '16 at 11:19
  • Is it possible to use the `glibc-headers` package on Visual Studio? I've been trying to look up how but am I correct in saying they won't work together? – Milan May 10 '16 at 13:09
0

There are endless possibilities for bit manipulation. So you should practice; but I'll give you some examples.

Below code will "set" only bit 3 of x (where bit 0 is the least significant bit):

#define SET_BIT3 (0x08)
x |= SET_BIT3;

Below code will "reset" only bit 3:

#define RESET_BIT3 (0xFFFFFFFFFFFFFFF7)
x &= RESET_BIT3;

Another thing you could do is make a union like this:

typedef union
{
    struct
    {
        unsigned BIT0:1;
        unsigned BIT1:1;
        unsigned BIT2:1;
        unsigned BIT3:1;
        unsigned BIT4:1;
        unsigned BIT5:1;
        unsigned BIT6:1;
        unsigned BIT7:1;
        unsigned BIT8:1;
        unsigned BIT9:1;
        unsigned BIT10:1;
        unsigned BIT11:1;
        unsigned BIT12:1;
        unsigned BIT13:1;
        unsigned BIT14:1;
        unsigned BIT15:1;
    };
    struct
    {
        unsigned BYTE0:8;
        unsigned BYTE1:8;
    };
    struct
    {
        unsigned ALL:16;
    };
}two_bytes;

What you can do with it is this:

two_bytes var;

var.ALL = 0; // clear all bits
var.BYTE1 = 0xFF; // make all bits of most significant byte 1
var.BIT7 = 1; // set only bit 7 

Or you can turn into bitwise operators again:

#define SET_BIT3 (0x08)
var.ALL |= SET_BIT3;
etugcey
  • 109
  • 2
  • 11
  • On many platforms `unsigned` is 4-byte wide. Use a portable type, like `uint16_t`. – Maxim Egorushkin May 10 '16 at 11:32
  • 4
    The union is undefined behaviour in standard C++. Bitfield layout is implementation-defined so this also would make it difficult to know you were getting the right byte. – M.M May 10 '16 at 11:34
  • @MaximEgorushkin Yes, but it is only an example type, and sometimes in embedded platforms, I prefer flexibility to portability. This is a common way manufacturers define registers of a microcontroller in their code. – etugcey May 10 '16 at 11:38