3

As the title says, the functionality I'm after is provided by C++11's math libraries to find the next floating point value towards a particular value.

Aside from pulling the code out of the std library (which I may have to resort to), any alternatives to do this with C++03 (using GCC 4.4.6)?

Dan
  • 33,953
  • 24
  • 61
  • 87

2 Answers2

3

Platform dependently, assuming IEEE754, and modulo endianness, you can store the data of the floating point number in an integer, increment by one, and retrieve the result:

float input = 3.15;

uint32_t tmp;

unsigned char * p = reinterpret_cast<unsigned char *>(&tmp);
unsigned char * q = reinterpret_cast<unsigned char *>(&input);

p[0] = q[0]; p[1] = q[1]; p[2] = q[2]; p[3] = q[3];  // endianness?!

++tmp;

q[0] = p[0]; q[1] = p[1]; q[2] = p[2]; q[3] = p[3];

return input;

Beware of zeros, NaNs and infinities, of course.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 1
    Note that to decrement a negative float, one must increment its unsigned integer representation. – Pascal Cuoq May 02 '13 at 10:52
  • 1
    [Here is a blog post discussing this approach.](http://randomascii.wordpress.com/2012/01/23/stupid-float-tricks-2/) – BoBTFish May 02 '13 at 10:53
  • What's wrong with a plain `std::copy(p, p+4, q)` ? Or, assuming that the alignment is equal, just `reinterpret_cast(input)++;` – MSalters May 02 '13 at 11:29
  • You're being a bit too complicated. `memcpy` does what you want. And on most machines, `reinterpret_cast( &input )`, or putting them into a `union`. (And endianness will only be an issue if floats and ints have different endianness---something I've never heard of.) – James Kanze May 02 '13 at 11:37
  • 3
    @MSalters: `std::copy` is fine. I didn't feel like using the library today. Your direct cast is UB. – Kerrek SB May 02 '13 at 13:44
  • @KerrekSB: Perhaps, but it was platform-dependent anyway and I've seen better code being generated that way (compiler keeping the variable in the same register). – MSalters May 02 '13 at 13:52
  • @MSalters: Hmm... typical processors have a separate set of registers for floating point numbers... – Kerrek SB May 02 '13 at 17:24
  • @KerrekSB: In hindsight, that is the smarter CPU design. It allows you to have more registers for the same instruction length, since the opcode helps disambiguate between int register 0 and FP register 0. But as x86 shows, ISA design is more art then science. – MSalters May 03 '13 at 09:18
  • @MSalters: Well, once you take the address of something, I suppose the thing has to be written to memory anyway... – Kerrek SB May 03 '13 at 09:35
  • @KerrekSB: That's the nice thing of `reinterpret_cast`, no address is involved. And on my target, which doesn't have distinct FP registers, it didn't even involve a register-to-register copy. – MSalters May 03 '13 at 10:34
  • [using `reinterpret_cast` for type punning still invokes undefined behavior?](https://stackoverflow.com/q/53995657/995714) – phuclv Apr 11 '22 at 15:49
0

I realize this answer is quite late, but I stumbled upon the solution when I needed something very similar, so figured this should be included here for completeness. In C++11, the mentioned APIs are in the 'std' namespace. Prior to that, just remove the 'std' namespace qualifier, from math.h:

     double nextafter  (double x     , double y);
      float nextafterf (float x      , float y);
long double nextafterl (long double x, long double y);

Although I don't have GCC4.4.6 to validate, this did work for me on an old compiler that does not support C++11.

Nicole Finnie
  • 1,370
  • 13
  • 12