2015-10-28 3 views
3

Я пытаюсь написать обертку для ADO.Каков правильный тип в c C++ для хранения VT_DECIMAL COM?

DECIMAL один тип COM VARIANT может быть, когда VARIANT тип VT_DECIMAL.

Я пытаюсь поместить его в c родной тип данных и сохранить значение переменной. Кажется, что правильный тип длинный двойной, но я получаю «подходящую ошибку преобразования».

Например:

_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 также хорош для меня)

+0

C или C++? нет C \ C++ – NathanOliver

+0

C++, но мне нужен собственный тип данных c. – SHR

+1

Это уже ответили: http://stackoverflow.com/questions/24891663/c-converting-variant-decimal-to-double-value – GreatAndPowerfulOz

ответ

3

Внутреннее представление для DECIMAL не является двойной точности с плавающей запятой значение, это целое число, а не со знаком/масштаба вариантов. Если вы собираетесь инициализировать части DECIMAL, вы должны инициализировать эти поля - 96-битное целочисленное значение, масштаб, знак, после чего вы получите действительное десятичное значение VARIANT.

DECIMAL on MSDN:

  • масштаб - количество знаков после запятой для числа. Допустимые значения от 0 до 28. Таким образом, 12.345 представлено как 12345 со шкалой 3.
  • знак - обозначает знак; 0 для положительных чисел или DECIMAL_NEG для отрицательных чисел. Таким образом, -1 представляется как 1 с установленным битом DECIMAL_NEG.
  • Hi32 - Высокие 32 бита числа.
  • Lo64 - низкий 64-разрядный номер. Это _int64.

Ваши вопросы:

это совершенно безопасно использовать двойной хранить все возможные десятичные значения?

Вы не можете инициализировать в два раза непосредственно (например, VT_R8), но вы можете инициализировать, как двойной вариант и использовать конверсионный вариант API для преобразования в VT_DECIMAL. Небольшое округление можно применить к значению.

Если нет, то как можно преобразовать десятичную дробь в длинный двойной?

Как преобразовать десятичную строку в строку?(С помощью оператора < <, Sprintf также хорош для меня)

VariantChangeType может преобразовать десятичный вариант для варианта другого типа, в том числе целого, двойные, строк - вы обеспечиваете тип для преобразования в. И наоборот, вы также можете преобразовать что-то другое в десятичное.

+1

Я бы настоятельно посоветовал здесь использовать VariantChangeTypeEx вместо VariantChangeType, поскольку он позволяет указать локаль при использовании строк (для поддержки десятичных знаков и т. Д.). –

1

Если я понял, документация от Microsoft (https://msdn.microsoft.com/en-us/library/cc234586.aspx) правильно, VT_DECIMAL является точным 92-битное целое значение с фиксированным масштабом и точностью. В этом случае вы не можете сохранить это без потери информации в поплавке, двойной или 64-битной целочисленной переменной.

Лучше всего хранить его в 128-битном целочисленном виде, таком как __int128, но я не знаю уровня поддержки компилятора для него. Я также не уверен, что вы сможете просто бросить друг друга, не прибегая к некоторым манипуляциям.

1

Можно ли использовать двойное хранилище для сохранения всех возможных десятичных значений?

На самом деле это зависит от того, что вы подразумеваете под safe. Если вы имеете в виду «есть ли риск введения некоторой степени неточности конверсии?», Да, существует риск. Внутренние представления слишком отличаются друг от друга, гарантируя идеальное преобразование, и, скорее всего, будет внедрен шум преобразования.

Как преобразовать десятичную дробь в длинную двойную/строку?

Это зависит от того (опять же), что вы хотите сделать с объектом:

Для хранения без преобразования неточностей, вы, вероятно, следует хранить десятичное как масштабируется целое формы pair<long long,short>, где first содержит 96-битовую мантиссу, а second содержит число цифр справа от десятичной точки.Это представление максимально приближено к внутреннему представлению десятичной системы, не приведет к какой-либо конверсионной неточности и не будет тратить ресурсы ЦП на целочисленное форматирование.

+0

, поэтому я понимаю, что лучший способ - сохранить его в строчке, правильно? – SHR

+0

Для хранения с полной точностью я бы использовал либо строковое представление, либо масштабированное целочисленное представление: в основном представление, которое наилучшим образом соответствует внутреннему представлению VT_DECIMAL. См. Фрагмент, представленный ниже @milevyo, это, вероятно, еще лучше. –

1

«Безопасный» - это не совсем точное слово, точка DECIMAL - это не учитывать ошибки округления из-за базовых преобразований. Расчеты производятся в базе 10 вместо базы 2. Это делает их медленными, но точными, то, что нравится бухгалтеру. Ему не придется преследовать несоответствия в миллиард долларов.

Используйте _variant_t :: ChangeType(), чтобы сделать преобразования. Перейдите VT_R8, чтобы преобразовать в двойную точность. Передайте VT_BSTR, чтобы преобразовать в строку, что нравится бухгалтеру. Нет смысла в погоне long double, что 10-байтный тип FPU - это история.

+0

, поэтому я понимаю, что лучший способ - сохранить его как строку, правильно? – SHR

+1

Лучший способ - сохранить его DECIMAL. Ваш вопрос совершенно неясен в отношении точной причины, по которой вы хотите его преобразовать. Я дал подсказки, необходимые для его правильного преобразования. Только конвертировать один раз. –

1

это фрагменты взяты из 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; 
} 
Смежные вопросы