Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert a double to a C# decimal in C++?

Given the reprensentation of decimal I have --you can find it here for instance--, I tried to convert a double this way:

explicit Decimal(double n)
{
    DoubleAsQWord doubleAsQWord;
    doubleAsQWord.doubleValue = n;
    uint64 val = doubleAsQWord.qWord;

    const uint64 topBitMask = (int64)(0x1 << 31) << 32;

    //grab the 63th bit
    bool isNegative = (val & topBitMask) != 0;

    //bias is 1023=2^(k-1)-1, where k is 11 for double
    uint32 exponent = (((uint64)(val >> 31) >> 21) & 0x7FF) - 1023;

    //exclude both sign and exponent (<<12, >>12) and normalize mantissa
    uint64 mantissa = ((uint64)(0x1 << 31) << 21) | (val << 12) >> 12;

    // normalized mantissa is 53 bits long,
    // the exponent does not care about normalizing bit
    uint8 scale = exponent + 11; 
    if (scale > 11)
        scale = 11;
    else if (scale < 0)
        scale = 0;
    lo_ = ((isNegative ? -1 : 1) * n) * std::pow(10., scale);
    signScale_ = (isNegative ? 0x1 : 0x0) | (scale << 1);

    // will always be 0 since we cannot reach
    // a 128 bits precision with a 64 bits double
    hi_ = 0;
}

The DoubleAsQWord type is used to "cast" from double to its uint64 representation:

union DoubleAsQWord
{
    double doubleValue;
    uint64 qWord;
};

My Decimal type has these fields:

uint64 lo_;
uint32 hi_;
int32 signScale_;

All this stuff is encapsulated in my Decimal class. You can notice I extract the mantissa even if I'm not using it. I'm still thinking of a way to guess the scale accurately.

This is purely practical, and seems to work in the case of a stress test:

BOOST_AUTO_TEST_CASE( convertion_random_stress )
{
    const double EPSILON = 0.000001f;

    srand(time(0));
    for (int i = 0; i < 10000; ++i)
    {
        double d1 = ((rand() % 10) % 2 == 0 ? -1 : 1)
            * (double)(rand() % 1000 + 1000.) / (double)(rand() % 42 + 2.);
        Decimal d(d1);

        double d2 = d.toDouble();

        double absError = fabs(d1 - d2);
        BOOST_CHECK_MESSAGE(
            absError <= EPSILON,
            "absError=" << absError << " with " << d1 << " - " << d2
        );
    }
}

Anyway, how would you convert from double to this decimal representation?

like image 633
mister why Avatar asked May 19 '11 11:05

mister why


People also ask

What is double in C?

A double is a data type in C language that stores high-precision floating-point data or numbers in computer memory. It is called double data type because it can hold the double size of data compared to the float data type. A double has 8 bytes, which is equal to 64 bits in size.

What does type casting C require?

Type Casting is basically a process in C in which we change a variable belonging to one data type to another one. In type casting, the compiler automatically changes one data type to another one depending on what we want the program to do.


2 Answers

I think you guys will be interested in an implementation of a C++ wrapper to the Intel Decimal Floating-Point Math Library:

C++ Decimal Wrapper Class

Intel DFP

like image 179
mister why Avatar answered Oct 08 '22 20:10

mister why


What about using VarR8FromDec Function ?

EDIT: This function is declared on Windows system only. However an equivalent C implementation is available with WINE, here: http://source.winehq.org/source/dlls/oleaut32/vartype.c

like image 2
Simon Mourier Avatar answered Oct 08 '22 21:10

Simon Mourier