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

荒凉一梦 提交于 2021-01-27 06:45:38

问题


I'm trying to write a wrapper to ADO.

A DECIMAL is one type a COM VARIANT can be, when the VARIANT type is VT_DECIMAL.

I'm trying to put it in c native data type, and keep the variable value. it seem that the correct type is long double, but I get "no suitable conversion error".

For example:

_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.  
}

So my questions are:

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

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

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


回答1:


The internal representation for DECIMAL is not a double precision floating point value, it is integer instead with sign/scale options. If you are going to initialize DECIMAL parts, you should initialize these fields - 96-bit integer value, scale, sign, then you get valid decimal VARIANT value.

DECIMAL on MSDN:

  • scale - The number of decimal places for the number. Valid values are from 0 to 28. So 12.345 is represented as 12345 with a scale of 3.
  • sign - Indicates the sign; 0 for positive numbers or DECIMAL_NEG for negative numbers. So -1 is represented as 1 with the DECIMAL_NEG bit set.
  • Hi32 - The high 32 bits of the number.
  • Lo64 - The low 64 bits of the number. This is an _int64.

Your questions:

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

You cannot initialize as double directly (e.g. VT_R8), but you can initialize as double variant and use variant conversion API to convert to VT_DECIMAL. A small rounding can be applied to value.

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 can convert decimal variant to variant of another type, including integer, double, string - you provide the type to convert to. Vice versa, you can also convert something different to decimal.




回答2:


"Safe" isn't exactly the correct word, the point of DECIMAL is to not introduce rounding errors due to base conversions. Calculations are done in base 10 instead of base 2. That makes them slow but accurate, the kind of accuracy that an accountant likes. He won't have to chase a billionth-of-a-penny mismatches.

Use _variant_t::ChangeType() to make conversions. Pass VT_R8 to convert to double precision. Pass VT_BSTR to convert to a string, the kind that the accountant likes. No point in chasing long double, that 10-byte FPU type is history.




回答3:


this snippets is taken from http://hackage.haskell.org/package/com-1.2.1/src/cbits/AutoPrimSrc.c

the Hackage.org says:

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

but please check the authors permissions

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



回答4:


If I understood Microsoft's documentation (https://msdn.microsoft.com/en-us/library/cc234586.aspx) correctly, VT_DECIMAL is an exact 92-bit integer value with a fixed scale and precision. In that case you can't store this without loss of information in a float, a double or a 64-bit integer variable.

You're best bet would be to store it in a 128-bit integer like __int128 but I don't know the level of compiler support for it. I'm also not sure you will be able to just cast one to the other without resorting to some bit manipulations.




回答5:


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

It actually depends what you mean by safe. If you mean "is there any risk of introducing some degree of conversion imprecision?", yes there is a risk. The internal representations are far too different to guarantee perfect conversion, and conversion noise is likely to be introduced.

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

It depends (again) of what you want to do with the object:

  • For floating-point computation, see @Gread.And.Powerful.Oz's link to the following answer: C++ converting Variant Decimal to Double Value
  • For display, see MSDN documentation on string conversion

For storage without any conversion imprecision, you should probably store the decimal as a scaled integer of the form pair<long long,short>, where first holds the 96-bits mantissa and second holds the number of digits to the right of the decimal point. This representation is as close as possible to the decimal's internal representation, will not introduce any conversion imprecision and won't waste CPU resources on integer-to-string formatting.



来源:https://stackoverflow.com/questions/33396728/what-is-the-correct-type-in-c-c-to-store-a-coms-vt-decimal

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!