2017-02-14 17 views
5

У меня есть поле ulong (я не могу изменить это). Мне нужно настроить это поле на сумму long (может быть также int). Однако я не могу добавить эти данные: Operator '+' is ambiguous on operands of type 'ulong' and 'long'.Добавьте длинный и улунг в C#

Я не могу преобразовать long к ulong, потому что я потеряю знак, я не могу преобразовать ulong к long, потому что я мог потерять часть стоимости.

В конечном счете, я хочу попытаться отрегулировать значение, если это вызовет обертку, которую я хочу вернуть false, а не завершить настройку. Если это не завершает, я хочу закончить настройку и вернуть true; Я могу сделать эту часть, но только в том случае, если я смогу найти способ объединить два поля в первую очередь.

public ulong Value; 
public bool AdjustValue(long amount) 
{ 
    // Operator '+' is ambiguous on operands of type 'ulong' and 'long' 
    ulong adjustValue = Value + amount; 

    if (amount < 0 && adjustValue > Value) 
     return false; 
    if (amount > 0 && adjustValue < Value) 
     return false; 

    Value = adjustValue; 
    return true; 
} 

Как это можно решить?

+0

Не ясно, что вы хотите сделать, но, возможно, вы можете найти решение с помощью Класс BigInteger из System.Numerics – Steve

+5

'if (long <0) ulong - = (ulong) (- long); else ulong + = (ulong) long; '? – itsme86

+0

@ itsme86 - отлично, я пробовал это, но пропустил '-long часть! Thanks –

ответ

4

Для удобства чтения, вы можете предпочесть предложение в комментарии:

adjustValue = Value; 
if (amount < 0) 
    adjustValue -= (ulong)(-amount); 
else 
    adjustValue += (ulong)amount; 

Это ясно, как вы ожидаете, что математика работать.

Однако & hellip;

Представление целых чисел практически в каждом современном аппаратном процессоре является дополнением к двум. Одна из причин заключается в том, что математика, использующая двоичные числа с двумя дополнениями, имеет очень приятное свойство: вы получаете одно и то же двоичное представление для сложения и вычитания, независимо от того, имеете ли вы значения со знаком или без знака.

Это означает, что вы можете просто изменить значение корректировки до ulong, чтобы сделать ваше дополнение. Согласно правилам языка C#, трансляция просто переинтерпретирует двоичное представление подписанного значения как unsigned. И добавление значения unsigned будет иметь точно такой же эффект, как если бы вы добавили значение с этим же двоичным представлением.

Вот пример кода, который показывает эту работу:

static void Main(string[] args) 
{ 
    ulong[] values = { ulong.MinValue, ulong.MaxValue, long.MaxValue }; 
    long[] adjust = { 0, 1, -1, long.MinValue, long.MaxValue }; 

    for (int i = 0; i < values.Length; i++) 
    { 
     for (int j = 0; j < adjust.Length; j++) 
     { 
      ulong value = values[i]; 

      bool result = AdjustValue(adjust[j], ref value); 
      Console.WriteLine($"{values[i]} + {adjust[j]} = {values} (overflow: {!result})"); 
     } 
    } 
} 

static bool AdjustValue(long amount, ref ulong value) 
{ 
    ulong adjustValue = value + (ulong)amount; 

    if (amount < 0 && adjustValue > value) 
     return false; 
    if (amount > 0 && adjustValue < value) 
     return false; 

    value = adjustValue; 
    return true; 
} 

Примечание: по умолчанию, математические операции во время выполнения не отмечены. Однако есть компилятор, который может изменить это. Если вы компилируете свой код с включенным этим переключателем, заставляя проверенные арифметические операции, вам необходимо обернуть это в блоке unchecked, чтобы избежать переполнения исключений.

0

Кто-то будет ненавидеть этот ответ, но вы можете использовать десятичный тип данных, так как он способен хранить 28-29 значащих цифр, а 2^64 имеет только 20 цифр.

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

public static ulong Add(this ulong baseValue, long offset) 
{ 
    decimal adjustedValue = (decimal)baseValue + offset; 
    return (ulong)adjustedValue; 
} 

Если переполнение, то система будет бросать достаточно приличный ArgumentOutOfRangeException, но вы также можете свернуть свой собственный:

public static ulong Add(this ulong baseValue, long offset) 
{ 
    decimal adjustedValue = (decimal)baseValue + offset; 
    if (adjustedValue > ulong.MaxValue) throw new ArgumentOutOfRangeException("Too big to fit!"); 
    if (adjustedValue < ulong.MinValue) throw new ArgumentOutOfRangeException("Too small to fit!"); 
    return (ulong)adjustedValue; 
}