提问人:TotalZero 提问时间:6/13/2023 最后编辑:phuclvTotalZero 更新时间:6/14/2023 访问量:97
存储无符号多头的增量
Storing deltas for Unsigned Longs
问:
我正在编写一个类,该类具有一堆数据类型的度量属性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
}
答:
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。如果您只需要像这样存储数据,请不要使用,或者会更好,因为它们可以存在于堆栈上class
struct
record
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.sign
operator >
)
0赞
Jeanot Zubler
6/13/2023
您忘记更改运算符的实现。此外,运算符中的三元语句可以缩短为<
>
a.sign ^ (a.magnitude > b.magnitude)
0赞
phuclv
6/14/2023
@JeanotZubler谢谢。我在实现中考虑过,但没有考虑过a.sign ^...
operator+
<
评论