Storing deltas for Unsigned Longs

115 Views Asked by At

I am writing a class that has a bunch of metric properties of ulong data type

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

The reason I use ulong is because it's a fact that those values are unsigned and the capacity of signed will not be enough.

But I also initiate another instance of this class to store Deltas of those values, the change between the last check and the current.

The problem I am having is that sometimes the number can go down, thus the delta is a negative value, but I cannot store it on the ulong. Even if I declare the Delta as a regular LONG, if number goes up to the ulong max from 0 it will not fit and will throw an error.

How could I accomplish storing the delta value of a ulong knowing this?

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

}
2

There are 2 best solutions below

3
phuclv On BEST ANSWER

Adding/subtracting two 64-bit numbers produces a 65-bit result, so just use Int128 if you use .NET Core 7.0 Preview 5 or newer. And don't use class if you just need to store data like this, a struct or record would be far better because they can live on stack

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

If you're on an old .NET framework then the most efficient solution is to implement your own 65-bit data type. It should be very simple because for printing and sorting purposes multiplication and division won't be needed. You just need to implement +/- and comparison operators like this

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);
}
6
Ihdina On

Try use 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);

    }
}

Result:

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