c\c++ 中存储 COM VT_DECIMAL 的正确类型是什么?

What is the correct type in c\c++ to store a COM's VT_DECIMAL?

我正在尝试为 ADO 编写包装程序。

一个DECIMAL是一个类型COM VARIANT可以,当VARIANT类型是VT_DECIMAL.

我正在尝试将其放入 c 本机数据类型中,并保留变量值。 似乎正确的类型是 long double,但我得到 "no suitable conversion error".

例如:

_variant_t v;
...

if(v.vt == VT_DECIMAL)
{
  double d = (double)v; //this works but I'm afraid can be loss of data...
  long double ld1 = (long double)v; //error: more then one conversion from variant to long double applied.
  long double ld2 = (long double)v.decVal; //error: no suitable conversion function from decimal to long double exist.  
}

所以我的问题是:

  1. 使用 double 存储所有可能的十进制值是否完全安全?

  2. 如果不是,我如何将小数转换为长双精度数?

  3. 如何将小数转换为字符串? (使用 << 运算符,sprintf 对我也有好处)

如果我正确理解 Microsoft 的文档 (https://msdn.microsoft.com/en-us/library/cc234586.aspx),VT_DECIMAL 是一个精确的 92 位整数值,具有固定的标度和精度。在那种情况下,您不能在不丢失信息的情况下将其存储在浮点数、双精度数或 64 位整数变量中。

你最好的选择是将它存储在像 __int128 这样的 128 位整数中,但我不知道编译器对它的支持级别。我也不确定您是否能够在不诉诸一些位操作的情况下将一个转换为另一个。

Is it totally safe to use double to store all possible decimal values?

这实际上取决于您所说的安全是什么意思。如果您的意思是 "is there any risk of introducing some degree of conversion imprecision?",是的,存在风险。内部表示差异太大,无法保证完美转换,并且很可能会引入转换噪声。

How can I convert the decimal to a long double / a string?

这取决于(再​​次)您想对对象做什么:

  • 对于浮点计算,请参阅@Gread.And.Powerful.Oz对以下答案的link:C++ converting Variant Decimal to Double Value
  • 显示见MSDN documentation字符串转换

对于没有任何转换不精确的存储,您可能应该将小数存储为 pair<long long,short> 形式的缩放整数,其中 first 保存 96 位尾数,second 保存小数点右边的位数。此表示尽可能接近十进制的内部表示,不会引入任何转换不精确,也不会浪费 CPU 整数到字符串格式的资源。

"Safe" 不是完全正确的词,DECIMAL 的要点是不引入因基数转换而导致的舍入误差。计算是以 10 为基数而不是以 2 为基数进行的。这使得计算速度缓慢但准确,这是会计师喜欢的那种准确度。他将不必追逐十亿分之一便士的不匹配。

使用 _variant_t::ChangeType() 进行转换。传递 VT_R8 以转换为双精度。传VT_BSTR转成字符串,会计喜欢的那种。追逐 long double 毫无意义,10 字节 FPU 类型已成为历史。

此片段摘自 http://hackage.haskell.org/package/com-1.2.1/src/cbits/AutoPrimSrc.c

Hackage.org 说:

Hackage is the Haskell community's central package archive of open source software.

但请检查作者权限

void writeVarWord64( unsigned int hi, unsigned int lo, VARIANT* v )
{
   ULONGLONG r;

   r = (ULONGLONG)hi;
   r >>= 32;
   r += (ULONGLONG)lo;

   if (!v) return;
   VariantInit(v);
   v->vt = VT_DECIMAL;
   v->decVal.Lo64  = r;
   v->decVal.Hi32  = 0;
   v->decVal.sign  = 0;
   v->decVal.scale = 0;
}

DECIMAL 的内部表示不是双精度浮点值,而是带有 sign/scale 选项的整数。如果您要初始化 DECIMAL 个部分,您应该初始化这些字段 - 96 位整数值、比例、符号,然后您将获得有效的十进制 VARIANT 值。

DECIMAL on MSDN:

  • scale - 数字的小数位数。有效值为 0 到 28。因此 12.345 表示为 12345,小数位数为 3。
  • sign - 表示标志; 0 表示正数,DECIMAL_NEG 表示负数。所以 -1 表示为 1,设置了 DECIMAL_NEG 位。
  • Hi32 - 数字的高 32 位。
  • Lo64 - 数字的低 64 位。这是一个 _int64。

您的问题:

is it totally safe to use double to store all possible decimal values?

您不能直接初始化为double(例如VT_R8),但您可以初始化为double变体并使用变体转换API转换为VT_DECIMAL。可以对值应用小的舍入。

if not, how can I convert the decimal to a long double?

How to convert a decimal to string? (using the << operator, sprintf is also good for me)

VariantChangeType 可以将十进制变体转换为另一种类型的变体,包括整数、双精度、字符串 - 您提供要转换为的类型。反之亦然,你也可以将不同的东西转换为十进制。