如何在 C++ 中将双精度转换为 C# 十进制

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

本文关键字:转换 十进制 双精度 C++      更新时间:2023-10-16

鉴于我有十进制的改写 - 例如,你可以在这里找到它--,我试图以这种方式转换双精度:

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;
}

DoubleAsQWord 类型用于从 double "转换"到其 uint64 表示形式:

union DoubleAsQWord
{
    double doubleValue;
    uint64 qWord;
};

我的十进制类型具有以下字段:

uint64 lo_;
uint32 hi_;
int32 signScale_;

所有这些东西都封装在我的十进制类中。你可以注意到我提取了尾数,即使我不使用它。我还在想一种准确猜测比例的方法。

这纯粹是实用的,似乎在压力测试的情况下有效:

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
        );
    }
}

无论如何,您将如何从double转换为这种decimal表示?

我想你们会对英特尔十进制浮点数学库的C++包装器的实施感兴趣:

C++ 十进制包装类

英特尔 DFP

使用 VarR8FromDec 函数怎么样?

编辑:此功能仅在Windows系统上声明。但是,WINE 提供了等效的 C 实现,如下所示:http://source.winehq.org/source/dlls/oleaut32/vartype.c

也许你正在寻找System::Convert::ToDecimal()
http://msdn.microsoft.com/en-us/library/a69w9ca0%28v=vs.80%29.aspx

或者,您可以尝试将 Double 重新转换为十进制。

MSDN 中的一个示例。
http://msdn.microsoft.com/en-us/library/aa326763%28v=vs.71%29.aspx

// Convert the double argument; catch exceptions that are thrown.
void DecimalFromDouble( double argument )
{
    Object* decValue;
    // Convert the double argument to a Decimal value.
    try
    {
        decValue = __box( (Decimal)argument );
    }
    catch( Exception* ex )
    {
        decValue = GetExceptionType( ex );
    }
    Console::WriteLine( formatter, __box( argument ), decValue );
}

如果您无权访问 .Net 例程,那么这很棘手。 我自己为我的十六进制编辑器完成了此操作(以便用户可以使用"属性"对话框显示和编辑 C# 十进制值) - 有关详细信息,请参阅 http://www.hexedit.com。 此外,HexEdit的来源是免费提供的 - 请参阅我在 http://www.codeproject.com/KB/cpp/HexEdit.aspx 上的文章。

实际上我的例程在十进制和字符串之间转换,但您当然可以使用 sprintf 先将双精度转换为字符串。 (同样,当你谈论双精度时,我认为你明确指的是IEEE 64位浮点格式,尽管这是现在大多数编译器/系统使用的格式。

请注意,如果要精确处理所有有效的十进制值并为无法转换的任何值返回错误,则有一些陷阱,因为格式没有很好的文档记录。 (十进制格式确实令人敬畏,例如相同的数字可以有很多表示形式。

这是我将字符串转换为小数的代码。 请注意,它使用 GNU 多精度算术库(以 mpz_ 开头的函数)。 如果 String2Decimal 函数由于某种原因(例如值太大)失败,则显然会返回 false。 参数 'presult' 必须指向至少 16 个字节的缓冲区来存储结果。

bool String2Decimal(const char *ss, void *presult)
{
    bool retval = false;
    // View the decimal (result) as four 32 bit integers
    unsigned __int32 *dd = (unsigned __int32 *)presult;
    mpz_t mant, max_mant;
    mpz_inits(mant, max_mant, NULL);
    int exp = 0;                // Exponent
    bool dpseen = false;        // decimal point seen yet?
    bool neg = false;           // minus sign seen?
    // Scan the characters of the value
    const char *pp;
    for (pp = ss; *pp != ''; ++pp)
    {
        if (*pp == '-')
        {
            if (pp != ss)
                goto exit_func;      // minus sign not at start
            neg = true;
        }
        else if (isdigit(*pp))
        {
            mpz_mul_si(mant, mant, 10);
            mpz_add_ui(mant, mant, unsigned(*pp - '0'));
            if (dpseen) ++exp;  // Keep track of digits after decimal pt
        }
        else if (*pp == '.')
        {
            if (dpseen)
                goto exit_func;    // more than one decimal point
            dpseen = true;
        }
        else if (*pp == 'e' || *pp == 'E')
        {
            char *end;
            exp -= strtol(pp+1, &end, 10);
            pp = end;
            break;
        }
        else
            goto exit_func;       // unexpected character
    }
    if (*pp != '')
        goto exit_func;           // extra characters after end
    if (exp < -28 || exp > 28)
        goto exit_func;          // exponent outside valid range
    // Adjust mantissa for -ve exponent
    if (exp < 0)
    {
        mpz_t tmp;
        mpz_init_set_si(tmp, 10);
        mpz_pow_ui(tmp, tmp, -exp);
        mpz_mul(mant, mant, tmp);
        mpz_clear(tmp);
        exp = 0;
    }
    // Get max_mant = size of largest mantissa (2^96 - 1)
    //mpz_set_str(max_mant, "79228162514264337593543950335", 10); // 2^96 - 1
    static unsigned __int32 ffs[3] = { 0xFFFFffffUL, 0xFFFFffffUL, 0xFFFFffffUL };
    mpz_import(max_mant, 3, -1, sizeof(ffs[0]), 0, 0, ffs);
    // Check for mantissa too big.
    if (mpz_cmp(mant, max_mant) > 0)
        goto exit_func;      // value too big
    else if (mpz_sgn(mant) == 0)
        exp = 0;  // if mantissa is zero make everything zero
    // Set integer part
    dd[2] = mpz_getlimbn(mant, 2);
    dd[1] = mpz_getlimbn(mant, 1);
    dd[0] = mpz_getlimbn(mant, 0);
    // Set exponent and sign
    dd[3] = exp << 16;
    if (neg && mpz_sgn(mant) > 0)
        dd[3] |= 0x80000000;
    retval = true;   // indicate success
exit_func:
    mpz_clears(mant, max_mant, NULL);
    return retval;
}

这个怎么样:

1) 冲刺数成 S2)查找小数点(strchr),存储在IDX中3) atoi = 轻松获得整数部分,使用并集分离高/低4) 使用 strlen - idx 获取点后的位数

sprintf 可能会很慢,但您将在 2 分钟内输入得到解决方案......