Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
268 views
in Technique[技术] by (71.8m points)

c++ - std::decimal::decimal64 correct usage, g++ 4.6.3

I'm investigating the replacing of doubles in our code with g++'s std::decimal::decimal32/64/128 types for purpose of currency amounts and prices, however I'm getting stuck on the point of how to best input and output the data. Specifically, there do not appear to be any routines for converting to/from a string, and the stringstream mechanisms do not compile for these types. The only way I see doing this is to use double as an intermediate type, however surely this at least partially defeats the purpose of using the decimal types if we're always inputting and outputting via doubles?

I'm sure I'm not understanding something here so would welcome some feedback on how best to use these types.

Edit:

I have hacked together a couple of input/output routines, however I'm not really satisfied with either. The input is hardly robust (no scientific notation support) and the output routine is simplistic, not to mention inefficient due to the double conversion.

#define MAX_DEC_LEN 17

std::decimal::decimal64 FromString(const char* str)
{
    if (strlen(str) > MAX_DEC_LEN)
        throw std::runtime_error("bad input");
    char buf[MAX_DEC_LEN+1];
    strncpy(buf, str, MAX_DEC_LEN+1);
    char* point(NULL); 
    if ((point = strchr(buf, '.')) != NULL)
        *(point++) = '';
    std::decimal::decimal64 ret(atoi(buf));
    if (point != NULL && *point != '')
    {
        int exponent(strlen(point));
        long long unsigned coeff(atoi(point));
        std::decimal::decimal64 d2(std::decimal::make_decimal64(coeff, -exponent));
        if (*buf == '-')
            ret -= d2;
        else
            ret += d2;
    }
    return ret;    
}

std::string ToString(std::decimal::decimal64 dec)
{
    double d(std::decimal::decimal_to_double(dec));
    std::ostringstream oss;
    oss << std::fixed << std::setprecision(6) << d;
    return oss.str();
}

I'm really after something better for both of these, plus a round (to a particular precision) to round things off (pardon the pun)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The Decimal TR defines overloads for input and output in sections 3.2.10 and 3.2.11, respectively. From the looks of it they are not implemented by gcc. The decimal support in gcc is implemented in terms of libdecnum which provides routines to convert from and to strings. I would create a simple implementation of the I/O operators using them to get started.

Edit on 2012-10-17: Looking at formatting the decimal values in gcc's library (std::decimal::decimal64 and family) revealed that gcc unfortunately doesn't install either the decNumber library headers or the library. Furthermore, other sources of decNumber don't quite align with the version shipping with gcc. As a result, the only way I found so far to get decimal numbers formatted is to use explicit declarations and using the library build during compiling gcc.

The simple part is the source, although for a rather simple implementation. First, the declaration which would go into a suitable header, e.g. <decimal/decimal>:

std::ostream& operator<< (std::ostream&, std::decimal::decimal32 const&);
std::ostream& operator<< (std::ostream&, std::decimal::decimal64 const&);
std::ostream& operator<< (std::ostream&, std::decimal::decimal128 const&);

A fairly simple implementation of something doing some formatting, although without any options could look like this:

extern "C" char* decimal32ToString(std::decimal::decimal32 const*, char*);
extern "C" char* decimal64ToString(std::decimal::decimal64 const*, char*);
extern "C" char* decimal128ToString(std::decimal::decimal128 const*, char*);

std::ostream& operator<< (std::ostream& out,
                          std::decimal::decimal32 const& value)
{
    char buffer[128];
    decimal32ToString(&value, buffer);
    return out << buffer;
}

std::ostream& operator<< (std::ostream& out,
                          std::decimal::decimal64 const& value)
{
    char buffer[128];
    decimal64ToString(&value, buffer);
    return out << buffer;
}

std::ostream& operator<< (std::ostream& out,
                          std::decimal::decimal128 const& value)
{
    char buffer[128];
    decimal128ToString(&value, buffer);
    return out << buffer;
}

With this in place, there is still an issue that I didn't manage to build anything using decimals unless some level of optimization, i.e., -O1 (or higher), is used. There are unreferenced symbols unless optimization is used but this is entirely unrelated to printing the values. To get definitions of the functions used in the implementation, I needed to link to the libdecnumber.a library which is created while gcc is being build:

g++ -o prog decimal-io.cpp main.cpp <gcc-build-root>/libdecnumber/libdecnumber.a

Aside from this I have my own implementation of the Decimal TR with some extension and basing it on the decnumber library and implement I/O properly. My implementation hopefully will become publicly available at some point but not any time soon.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...