6

I would like to have the closest number below 1.0 as a floating point. By reading wikipedia's article on IEEE-754 I have managed to find out that the binary representation for 1.0 is 3FF0000000000000, so the closest double value is actually 0x3FEFFFFFFFFFFFFF.

The only way I know of to initialize a double with this binary data is this:

double a;
*((unsigned*)(&a) + 1) = 0x3FEFFFFF;
*((unsigned*)(&a) + 0) = 0xFFFFFFFF;

Which is rather cumbersome to use.

Is there any better way to define this double number, if possible as a constant?

Mark Dickinson
  • 29,088
  • 9
  • 83
  • 120
martinus
  • 17,736
  • 15
  • 72
  • 92

8 Answers8

8

Hexadecimal float and double literals do exist. The syntax is 0x1.(mantissa)p(exponent in decimal) In your case the syntax would be

double x = 0x1.fffffffffffffp-1
Shum
  • 1,236
  • 9
  • 22
  • I've never heard of this syntax before. Do you have a reference? – Mark Ransom Oct 16 '10 at 05:17
  • I think its part of the C99 standard. It works with GNU compilers, I don't about others. – Shum Oct 16 '10 at 05:32
  • @Mark Ransom: I wrote an article about this recently: http://www.exploringbinary.com/hexadecimal-floating-point-constants/ – Rick Regan Oct 16 '10 at 15:51
  • @Mark Ransom: Added in C99. Also supported in `printf`/`scanf` via the `%a` format specifier. By far the best way to specify floating-point values in C. – Stephen Canon Oct 18 '10 at 19:13
3

It's not safe, but something like:

double a;
*(reinterpret_cast<uint64_t *>(&a)) = 0x3FEFFFFFFFFFFFFFL;

However, this relies on a particular endianness of floating-point numbers on your system, so don't do this!

Instead, just put DBL_EPSILON in <cfloat> (or as pointed out in another answer, std::numeric_limits<double>::epsilon()) to good use.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • Treating it as an integer should make it endian-independent (unless you have one of those weird "mixed endian" systems) – Rick Regan Oct 16 '10 at 15:54
  • @Rick Regan: Who's to say that the "endianness" of your platform's representation of floating-point types is consistent with the representation of integer types? – Oliver Charlesworth Oct 16 '10 at 17:15
  • Theoretically you're right -- but do you have an example (besides the mixed-endian “soft floats”)? – Rick Regan Oct 16 '10 at 23:17
3
#include <iostream>
#include <iomanip>
#include <limits>
using namespace std;

int main()
{
    double const    x   = 1.0 - numeric_limits< double >::epsilon();

    cout
        << setprecision( numeric_limits< double >::digits10 + 1 ) << fixed << x
        << endl;
}
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
1

If you make a bit_cast and use fixed-width integer types, it can be done safely:

template <typename R, typename T>
R bit_cast(const T& pValue)
{
    // static assert R and T are POD types

    // reinterpret_cast is implementation defined,
    // but likely does what you expect
    return reinterpret_cast<const R&>(pValue);
}

const uint64_t target = 0x3FEFFFFFFFFFFFFFL;
double result = bit_cast<double>(target);

Though you can probably just subtract epsilon from it.

Community
  • 1
  • 1
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • Not sure why you went to the trouble of defining bit_cast when you could have just used `reinterpret_cast` directly. Still seems like a good solution. – Mark Ransom Oct 16 '10 at 03:48
  • @Mark: That wouldn't static assert both types are POD types, and would make it easier to break aliasing rules. (Admittedly I gave a more general solution than required; in this case just doing it directly works fine.) – GManNickG Oct 16 '10 at 03:57
0

It's a little archaic, but you can use a union. Assuming a long long and a double are both 8 bytes long on your system:

typedef union { long long a; double b } my_union;

int main()
{
    my_union c;
    c.b = 1.0;
    c.a--;
    std::cout << "Double value is " << c.b << std::endl;
    std::cout << "Long long value is " << c.a << std::endl;
}

Here you don't need to know ahead of time what the bit representation of 1.0 is.

socket puppet
  • 3,191
  • 20
  • 16
0

This 0x1.fffffffffffffp-1 syntax is great, but only in C99 or C++17.

But there is a workaround, no (pointer-)casting, no UB/IB, just simple math.

double x = (double)0x1fffffffffffff / (1LL << 53);

If I need a Pi, and Pi(double) is 0x1.921fb54442d18p1 in hex, just write

const double PI = (double)0x1921fb54442d18 / (1LL << 51);

If your constant has large or small exponent, you could use the function exp2 instead of the shift, but exp2 is C99/C++11 ... Use pow for rescue!

kevinjwz
  • 181
  • 6
0

Rather than all the bit juggling, the most direct solution is to use nextafter() from math.h. Thus:

#include <math.h>
double a = nextafter(1.0, 0.0); 

Read this as: the next floating-point value after 1.0 in the direction of 0.0; an almost direct encoding of "the closest number below 1.0" from the original question.

CvR
  • 151
  • 1
  • 7
0

https://godbolt.org/z/MTY4v4exz

typedef union { long long a; double b; } my_union;

int main()
{
    my_union c;
    c.b = 1.0;
    c.a--;
    std::cout << "Double value is " << c.b << std::endl;
    std::cout << "Long long value is " << c.a << std::endl;
}
vfdff
  • 1