Какой лучший способ представить System.Decimal в буферах протоколов?

Следуя из этого вопроса, какой был бы лучший способ представить объект System.Decimal в буфере протокола?

Ответ 1

Ну, protobuf-net просто справится с этим для вас; он исчерпывает свойства типов и имеет полную поддержку decimal. Поскольку нет прямого способа выражения decimal в proto, он не будет (в настоящее время) генерировать свойство decimal из файла .proto, но было бы неплохо настроить какой-то общий тип ( "BCL.Decimal" или аналогичный) и интерпретировать его как десятичное.

Что касается его представления - у меня был дискуссионный документ об этом (теперь устаревший, я подозреваю) в области виджета protobuf-net; теперь есть рабочая версия в protobuf-net, которая просто делает это для вас.

Без сомнения, мы с Джоном заберут это позже позже;-p

Версия protobuf-net этого (в .proto) - это что-то вроде (от здесь):

message Decimal {
  optional uint64 lo = 1; // the first 64 bits of the underlying value
  optional uint32 hi = 2; // the last 32 bis of the underlying value
  optional sint32 signScale = 3; // the number of decimal digits, and the sign
}

Ответ 2

У нас с Марком очень расплывчатые планы придумать библиотеку "общего PB-сообщения", так что вы можете представлять довольно распространенные типы (дату/время и десятичную пружину мгновенно на ум) обычным способом, с доступными преобразованиями .NET и Java (и все остальное, кто хочет внести свой вклад).

Если вы счастливы придерживаться .NET, и вы ищете компактность, я бы мог пойти с чем-то вроде:

message Decimal {

    // 96-bit mantissa broken into two chunks
    optional uint64 mantissa_msb = 1;
    optional uint32 mantissa_lsb = 2;

    required sint32 exponent_and_sign = 3;
}

Знак может быть просто представлен знаком показателя exponent_and_sign, причем показателем является абсолютное значение.

Предоставление обеим частям мантиссы необязательно означает, что 0 представляется очень компактно (но все же дифференцируется между 0m и 0.0000m и т.д.). exponent_and_sign также может быть необязательным, если мы действительно хотели.

Я не знаю о проекте Marc, но в моем порт я генерирую частичные классы, поэтому вы можете поместить преобразование между System.Decimal и Protobuf.Common.Decimal(или что-то еще) в частичный класс.

Ответ 3

Несколько проще реализовать подход, чем Jon или Marc, - сохранить его как 4 значения sint32, что удобно тривиально сопоставить с выводом Decimal.GetBits().

Файл прото будет выглядеть так:

message ProtoDecimal {
    sint32 v1 = 1;
    sint32 v2 = 2;
    sint32 v3 = 3;
    sint32 v4 = 4;
}

И конвертер будет:

public decimal ConvertToDecimal(AbideDecimal value)
{
    return new decimal(new int[] { value.V1, value.V2, value.V3, value.V4 });
}

public ProtoDecimal ConvertFromDecimal(decimal value)
{
    var bits = decimal.GetBits(value);
    return new ProtoDecimal 
    {
        V1 = bits[0],
        V2 = bits[1],
        V3 = bits[2],
        V4 = bits[3]
    }
}

Это может быть не так просто в других языках, но если вам нужно только нацелиться на С#, тогда он займет столько же максимум 16 байт, как и другой подход (хотя значения, подобные 0, могут быть не так компактно сохранены - я не знаю достаточно о сложных деталях того, как Protobuf хранит целые), будучи гораздо понятнее тупым программистам вроде меня :)

Очевидно, вам придется гонять на лошадях, если вы хотите проверить работоспособность, но я сомневаюсь, что в этом есть что-то особенное.

Ответ 4

Я собрал патч для protobuf-csharp-port с помощью hooks, который генерирует классы protobuf с собственными структурами Decimal и DateTime. Проводной формат мудрый, они представлены двумя "встроенными" прото-сообщениями.

Вот ссылка: https://code.google.com/p/protobuf-csharp-port/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Priority%20Milestone%20Owner%20Summary&groupby=&sort=&id=78

Ответ 5

Когда вы знаете, что у вас есть ограниченное количество десятичных знаков, вы можете использовать наименьшую возможную единицу в качестве целочисленного значения. Например, при обработке денег не требуется десятичный тип, а вместо этого можно определить использование центовых единиц. Тогда целое число со значением 2 будет ссылаться на 0.02 в любой используемой валюте.