Boost logo

Boost :

Subject: Re: [boost] Extracting floating point constant exponent/mantissa at compile time
From: Peder Holt (peder.holt_at_[hidden])
Date: 2009-04-16 13:51:35


2009/4/16 Joel Falcou <joel.falcou_at_[hidden]>

> I'm seeking a way to perform compile-time "bitwise" casting of floating
> value aka
> turning a float into an uint32_t value with the same bit pattern.
>
> Eg: -3.14f becoming 0xC048F5C3
>
> I saw a mpl::double_ in the vault but I can't make tail nor head of the
> trick used in the large macros.hpp file.
> Considering I only need the capability of storing a flaot as a bit pattern
> and don't need the additional arithmetic,
> can someone enlighten me on the process used in mpl::double ?
>

I'll try to explain the trick I used:

a floating point consists of a mantissa and an exponent.
In order to compute the floating point, we do the following:

mantissa/(2^precision-1)*(2^exponent)

mantissa/(2^precision-1) will always lie in the interval [0,1>

BOOST_MPL_MATH_DOUBLE does two things:
1: Deduces the mantissa as an integer
2: Deduces the exponent as an integer
3: Deduces the sign

We compute the mantissa in a manner similar to this:
value=abs(value);
if(value>1) {
   for(n=1;n<=512;n>>=1) {
     if(value>=2) value/=2;
     else break;
   }
}
else {
   for(n=1;n<=512;n>>=1) {
     if(value<0.5) value*=2;
     else break;
   }
}

value*=pow(2,BitsInMantissa-1);
int64 int_mantissa=int64(value);

This is the essence of what the macros does, except it does it a a giant
nested ternary statement.
See:
BOOST_MPL_MATH_DOUBLE_NORMALISE(value)

This is defined as:
#define BOOST_MPL_MATH_DOUBLE_NORMALISE(value) \
        ( \
            BOOST_MPL_MATH_DOUBLE_HAS_NEGATIVE_EXP(value) \
          ? BOOST_MPL_MATH_DOUBLE_REMOVE_NEGATIVE_EXP1(value) \
          : BOOST_MPL_MATH_DOUBLE_REMOVE_POSITIVE_EXP1(value) \
        )

We end up with a double that when multiplied with (2^precision-1) becomes
the mantissa.

Now comes the exponent
This is computed as follows:
value=abs(value);
int exponent=0;
if(value>1) {
   for(n=512;n>0;n<<=1) {
     if(value>pow(2,n)) {
         exponent+=n;
         value/=pow(2,n);
     }
   }
}
else {
   for(n=512;n>0;n<<=1) {
     if(value<pow(2,-n)) {
         exponent-=n;
         value/=pow(2,-n);
     }
   }
}
Again, done in macro form, look at
BOOST_MPL_MATH_DOUBLE_EXP_TO_INT
which again is a nested ternary statement

#define BOOST_MPL_MATH_DOUBLE_EXP_TO_INT(value) \
        ( \
            (BOOST_MPL_MATH_DOUBLE_REMOVE_SIGN(value) < 1.0) \
          ? BOOST_MPL_MATH_DOUBLE_NEGATIVE_EXP_TO_INT1(value) \
          : BOOST_MPL_MATH_DOUBLE_POSITIVE_EXP_TO_INT1(value) \
        )

The function relies on the following constants that represents the exponents
of double, that is, 1*2eN, where N is 512,256,128,...-256,-512
#define BOOST_MPL_MATH_DOUBLE_EXP512 1.3407807929942597099574024998206e+154
#define BOOST_MPL_MATH_DOUBLE_EXP256 1.1579208923731619542357098500869e+77
#define BOOST_MPL_MATH_DOUBLE_EXP128 3.4028236692093846346337460743177e+38
#define BOOST_MPL_MATH_DOUBLE_EXP64 18446744073709551616.0
#define BOOST_MPL_MATH_DOUBLE_EXP32 4294967296.0
#define BOOST_MPL_MATH_DOUBLE_EXP31 2147483648.0
#define BOOST_MPL_MATH_DOUBLE_EXP30 1073741824.0
#define BOOST_MPL_MATH_DOUBLE_EXP16 65536.0
#define BOOST_MPL_MATH_DOUBLE_EXP8 256.0
#define BOOST_MPL_MATH_DOUBLE_EXP4 16.0
#define BOOST_MPL_MATH_DOUBLE_EXP2 4.0
#define BOOST_MPL_MATH_DOUBLE_EXP1 2.0
#define BOOST_MPL_MATH_DOUBLE_EXP0 1.0
#define BOOST_MPL_MATH_DOUBLE_EXP_1 0.5
#define BOOST_MPL_MATH_DOUBLE_EXP_2 0.25
#define BOOST_MPL_MATH_DOUBLE_EXP_4 0.0625
#define BOOST_MPL_MATH_DOUBLE_EXP_8 0.00390625
#define BOOST_MPL_MATH_DOUBLE_EXP_16 0.0000152587890625
#define BOOST_MPL_MATH_DOUBLE_EXP_32 0.00000000023283064365386962890625
#define BOOST_MPL_MATH_DOUBLE_EXP_64 5.4210108624275221700372640043497e-20
#define BOOST_MPL_MATH_DOUBLE_EXP_128 2.9387358770557187699218413430556e-39
#define BOOST_MPL_MATH_DOUBLE_EXP_256 8.6361685550944446253863518628004e-78
#define BOOST_MPL_MATH_DOUBLE_EXP_512 7.4583407312002067432909653154629e-155

The trick I used there is not supported by the C++ standard, but many
compilers support it anyway.

boost/mpl/math/double_/aux_/get_value.hpp shows how to convert the above
integers back to the double format.

For float, this will be simpler, as you don't need to split the mantissa
into two 32 bit integers

You could also have a look at
boost/mpl/math/double_/string_to_double.hpp
which can be used even by compilers that does not support the above.
This header exposes string_c_to_double which is used like this:
string_c_to_double<1,'.',2,3,4,'E','-',5> to represent 1.234E-5

I hope this helps

Good luck,
Regards
Peder Holt

> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
>


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk