Alternative to C++11's std::nextafter and std::nexttoward for C++03?
Asked Answered
H

2

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)?

Hyperbaton answered 2/5, 2013 at 10:41 Comment(0)
I
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.

Imine answered 2/5, 2013 at 10:45 Comment(11)
Note that to decrement a negative float, one must increment its unsigned integer representation.Levigate
Here is a blog post discussing this approach.Arella
What's wrong with a plain std::copy(p, p+4, q) ? Or, assuming that the alignment is equal, just reinterpret_cast<uint32_t&>(input)++;Gere
You're being a bit too complicated. memcpy does what you want. And on most machines, reinterpret_cast<unsigned const*>( &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.)Roughdry
@MSalters: std::copy is fine. I didn't feel like using the library today. Your direct cast is UB.Imine
@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).Gere
@MSalters: Hmm... typical processors have a separate set of registers for floating point numbers...Imine
@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.Gere
@MSalters: Well, once you take the address of something, I suppose the thing has to be written to memory anyway...Imine
@KerrekSB: That's the nice thing of reinterpret_cast<uint32_t&>, 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.Gere
using reinterpret_cast for type punning still invokes undefined behavior?Segmentation
A
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.

Allo answered 11/4, 2022 at 15:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.