【问题标题】:What is the correct type in c\c++ to store a COM's VT_DECIMAL?c\c++ 中存储 COM 的 VT_DECIMAL 的正确类型是什么?
【发布时间】:2015-10-28 16:41:36
【问题描述】:

我正在尝试为 ADO 编写一个包装器。

VARIANT 类型为VT_DECIMAL 时,DECIMALCOM VARIANT 可以是一种类型。

我正在尝试将其放入 c 本机数据类型,并保留变量值。 似乎正确的类型是 long double,但我得到“没有合适的转换错误”。

例如:

_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. 使用双精度存储所有可能的十进制值是否完全安全?

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

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

【问题讨论】:

  • C 还是 C++?没有 C\C++
  • c++,但我需要 c 本机数据类型。
  • 我想你想要 C++ 简单类型。 C++ 不是“带类的 C”。
  • @Olaf A c 原生类型是可以放入extern "c" 方法的类型

标签: c++ com decimal ado


【解决方案1】:

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

DECIMAL on MSDN:

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

您的问题:

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

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

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

如何将小数转换为字符串? (使用

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

【讨论】:

  • 我强烈建议在这里使用 VariantChangeTypeEx 而不是 VariantChangeType 因为它允许在使用字符串时指定语言环境(用于十进制字符支持等)。
【解决方案2】:

“安全”不是完全正确的词,DECIMAL 的重点是不会由于基本转换而引入舍入错误。计算是以 10 为底而不是以 2 为底的。这使得它们缓慢但准确,这是会计师喜欢的那种准确性。他不必追逐十亿分之一的错配。

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

【讨论】:

  • 所以我知道最好的方法是将其保留为字符串,对吗?
  • 最好的方法是保持十进制。您的问题完全不清楚您要转换它的确切原因。我给出了正确转换它所需的提示。只转换一次。
【解决方案3】:

这个sn-ps取自http://hackage.haskell.org/package/com-1.2.1/src/cbits/AutoPrimSrc.c

Hackage.org 说:

Hackage 是 Haskell 社区的主要开放包存档 源软件。

但请检查作者权限

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

【讨论】:

  • 你的程序好复杂!这可以写成3行!使用 Lo32 和 Mid32 而不是 Lo64 !
【解决方案4】:

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

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

【讨论】:

    【解决方案5】:

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

    这实际上取决于您所说的安全。如果您的意思是“是否存在引入某种程度的转换不精确的风险?”,是的,存在风险。内部表示差异太大,无法保证完美转换,很可能会引入转换噪声。

    如何将小数转换为长双精度/字符串?

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

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

    【讨论】:

    • 所以我明白最好的方法是把它保留为一个字符串,对吗?
    • 为了以全精度存储,我将使用字符串表示或缩放整数表示:基本上是与 VT_DECIMAL 的内部表示最匹配的表示。请参阅@milevyo 下面提供的 sn-p,它可能会更好。
    猜你喜欢
    • 2013-06-01
    • 1970-01-01
    • 2018-10-12
    • 1970-01-01
    • 2014-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    相关资源
    最近更新 更多