在C#中添加long和ulong

我正在使用的api有一个ulong字段(我不能改变它)。 我需要long调整这个字段(也可以是一个int )。 但是我无法添加这些togeather: Operator '+' is ambiguous on operands of type 'ulong' and 'long'

我无法将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  Value) return false; if (amount > 0 && adjustValue < Value) return false; Value = adjustValue; return true; } 

我怎么解决这个问题?

为了便于阅读,您可能更喜欢评论中的建议:

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

这清楚地表明了您对数学的期望。

然而…

实际上每个现代硬件CPU中的整数表示都是两个补码。 其中一个原因是使用二进制补码二进制数的数学具有非常好的属性:无论是处理有符号值还是无符号值,都可以获得相同的加法和减法二进制表示。

这意味着您可以将调整值ulong转换为ulong以进行添加。 根据C#语言规则,强制转换将简单地将签名值的二进制表示重新解释为无符号。 添加无符号值将具有与使用相同二进制表示添加带符号值完全相同的效果。

这是一个显示此工作的代码示例:

 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块中,以避免出现溢出exception。

有人会讨厌这个答案,但你可以使用十进制数据类型,因为它能够存储28-29个有效数字,而2 ^ 64只能存储20个数字。

自然地使用浮点数据类型会在性能方面产生一些开销,但另一方面,这个代码非常容易阅读,并且可能比需要的解决方案具有更好的分支预测/流水线。

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