存储无符号多头的增量

Storing deltas for Unsigned Longs

提问人:TotalZero 提问时间:6/13/2023 最后编辑:phuclvTotalZero 更新时间:6/14/2023 访问量:97

问:

我正在编写一个类,该类具有一堆数据类型的度量属性ulong

class Metrics {
  public ulong Memory
  public ulong Handles
  public ulong Calls
}

我之所以使用,是因为这些值是无符号的,签名的容量是不够的。ulong

但是我还启动了此类的另一个实例来存储这些值的增量,即上次检查和当前检查之间的变化。

我遇到的问题是有时数字会下降,因此增量是负值,但我无法将其存储在 . 即使我将 Delta 声明为常规 LONG,如果数字从 0 上升到 ulong 最大值,它也不会适合并会抛出错误。ulong

知道这一点,我怎么能完成存储 ulong 的 delta 值?

class myData {
   public Metrics Deltas
   public Metrics Data

   ulong myValue = ulong.MaxValue;
   Deltas.Calls = (myValue - Data.Calls);
   Data.Calls = myValue;
   // Delta will be +MaxValue

   myValue = 0;
   Deltas.Calls = (myValue - Data.Calls);
   Data.Calls = myValue;
   // Delta will be -MaxValue, unsigned, cannot store it

}
C# 符号无 符号整数

评论

1赞 paulsm4 6/13/2023
问:为什么不 a) 在一个类实例中将“当前”值存储为 ulong,b) 在另一个不同的实例中将“新”值存储为 ulong,以及 c) 使用有符号算术动态计算增量?PS:Win32的“ulong”是32位。但是 C# ulong 是 64 位:learn.microsoft.com/en-us/dotnet/csharp/language-reference/...。问:你真的需要 64 位吗?
1赞 Jeremy Lakeman 6/13/2023
然后,不要在已检查的上下文中使用已检查的操作。
0赞 Dave S 6/13/2023
您需要知道符号还是只知道 abs(delta)?
0赞 TotalZero 6/13/2023
@DaveS我需要知道这个标志,知道它是上升还是下降。
0赞 TotalZero 6/13/2023
@paulsm4 这是一个很好的观点,我也尝试过,但即便如此,当使用 64 位 ulong(是的,我需要那么长)时,我无法减去 0 - ulong。最大值并将其存储在有符号的数据类型上,对吗?

答:

2赞 Ihdina 6/13/2023 #1

尝试使用 BigInteger

using System;
using System.Numerics;

public struct Metrics {
  public BigInteger Memory { get; set; }
  public BigInteger Handles { get; set; }
  public BigInteger Calls { get; set; }
}

public class Program
{   
    public static void Main()
    {
        Metrics deltas = new Metrics();     
        Metrics data = new Metrics();

        Console.WriteLine("data.Calls: " + data.Calls);

        BigInteger myValue = UInt64.MaxValue;
        Console.WriteLine("myValue (UInt64.MaxValue): " + myValue);

        deltas.Calls = myValue - data.Calls;    
        Console.WriteLine("deltas.Calls (myValue-data.Calls): " + deltas.Calls);

        data.Calls = myValue;
        Console.WriteLine("data.Calls (myValue): " + data.Calls);
       
        
        Console.WriteLine("\n");
                
        Console.WriteLine("data.Calls: " + data.Calls);

        myValue = 0;
        Console.WriteLine("myValue: " + myValue);

        deltas.Calls = myValue - data.Calls;
        Console.WriteLine("deltas.Calls (myValue-data.Calls): " + deltas.Calls);

        data.Calls = myValue;
        Console.WriteLine("data.Calls (myValue): " + data.Calls);

    }
}

结果:

data.Calls: 0
myValue (UInt64.MaxValue): 18446744073709551615
deltas.Calls (myValue-data.Calls): 18446744073709551615
data.Calls (myValue): 18446744073709551615

data.Calls: 18446744073709551615
myValue: 0
deltas.Calls (myValue-data.Calls): -18446744073709551615
data.Calls (myValue): 0

评论

0赞 TotalZero 6/13/2023
可爱,老实说,我不知道该数据类型,好技巧,按预期工作。
1赞 phuclv 6/13/2023
BigInteger 对于这个来说太贵了。Int128 更快、更小
0赞 Ihdina 6/13/2023
@phuclv,正确。如果我们知道要存储的数据的最高限制,则使用内存高于接近最高限制的变量会更好,因为它可以节省内存,但是如果我们不知道最高限制,那么使用 BigInteger 更安全。示例 UInt128.MaxValue * 使用 UInt128 存储结果时,UInt128.MaxValue 将失败,但使用 BigInteger 时不会失败。
1赞 phuclv 6/13/2023
@Ihdina限制是已知的,即 [-ulong.MaxValue,乌龙。MaxValue]。加/减 64 位数字时不能超过 65 位
0赞 Ihdina 6/13/2023
@phuclv 如果他们只使用减法运算,他们可以使用 Int128,因为它绰绰有余。但是当有乘法运算时,您应该使用 BigInteger,以免它溢出。
4赞 phuclv 6/13/2023 #2

将两个 64 位数字相加/相减会生成 65 位结果,因此,如果使用 .NET Core 7.0 预览版 5 或更高版本,则只需使用 Int128。如果您只需要像这样存储数据,请不要使用,或者会更好,因为它们可以存在于堆栈上classstructrecord

struct Metrics {
  public Int128 Memory
  public Int128 Handles
  public Int128 Calls
}

class myData {
   public Metrics Deltas
   public Metrics Data

   Int128 myValue = ulong.MaxValue;
   Deltas.Calls = myValue - Data.Calls;
   Data.Calls = myValue;

   myValue = 0;
   Deltas.Calls = myValue - Data.Calls;
   Data.Calls = myValue;
}

如果您使用的是旧的 .NET Framework,那么最有效的解决方案是实现您自己的 65 位数据类型。它应该非常简单,因为出于打印和排序目的,不需要乘法和除法。你只需要实现这样的 / 和比较运算符+-

public readonly struct Delta
{
    private readonly ulong magnitude;
    private readonly bool sign; // true: negative

    public Delta(ulong magn, bool sgn)
    {
        sign = sgn;
        magnitude = magn;
    }
    public Delta(ulong a)
    {
        sign = false;
        magnitude = a;
    }

    public static Delta operator +(Delta a) => a;
    public static Delta operator -(Delta a) => new Delta(a.magnitude, !a.sign);

    public static Delta operator +(Delta a, Delta b)
    {
        if (a.sign == b.sign)
        {
            var m = a.magnitude + b.magnitude;
            if (m < a.magnitude) // overflow
            {
                sign = !sign;
            }
            return new Delta(m, a.sign);
        }
        var max = Math.Max(a.magnitude, b.magnitude);
        var min = Math.Min(a.magnitude, b.magnitude);
        var sign = a.sign;
        var m = max.magnitude - min.magnitude;
        if (m > max.magnitude) // overflow
        {
            sign = !sign;
        }
        
        return new Delta(max - min, sign);
    }
    public static Delta operator -(Delta a, Delta b) => a + (-b);
    
    public static bool operator ==(Delta a, Delta b)
    {
        return a.magnitude == b.magnitude && a.sign == b.sign;
    }
    public static bool operator !=(Delta a, Delta b) => !(a == b);
    
    public static bool operator >(Delta a, Delta b)
    {
        return a.sign == b.sign ? a.sign ^ (a.magnitude > b.magnitude) b.sign;
    }
    public static bool operator <(Delta a, Delta b) => !(a > b);
    
    public override string ToString()
    {
        return sign ? $"-{magnitude}" : $"{magnitude}";
    }
}

 // Get delta of a and b
public Delta GetDelta(ulong a, Delta ulong b)
{
    return Delta(a) - Delta(b);
}

评论

0赞 Oliver 6/13/2023
一个非常好的 65 位数据类型的实现。+1 (我喜欢这个简写return b.signoperator >)
0赞 Jeanot Zubler 6/13/2023
您忘记更改运算符的实现。此外,运算符中的三元语句可以缩短为<>a.sign ^ (a.magnitude > b.magnitude)
0赞 phuclv 6/14/2023
@JeanotZubler谢谢。我在实现中考虑过,但没有考虑过a.sign ^...operator+<