I like to program a histogram function using a class with a generic data type bound to an INumber in .NET 7/C# 11 as
public class Histogram<TValue> where TValue : INumber<TValue>
public List<KeyValuePair<int, int>> CalcHistogram(List<TValue> Values, TValue CorrectValue, TValue BinSize)
{
// ...
foreach (TValue value in Values)
{
// version 1
// calc bin number as TValue type
TValue bn = (value - CorrectValue) / BinSize;
// round to nearest int without Math.Round
int binNumber = bn < 0 ? (int)(bn - 0.5) : (int)(bn + (TValue)0.5);
// version 2
// calc bin number
int binNumber2 = (int)Math.Round((value - CorrectValue) / BinSize, MidpointRounding.AwayFromZero);
// ....
But I get multiple errors I don't understand:
bn < 0results in a CS0019 error (Operator '<' cannot be applied to operands of type 'TValue' and 'int'), although I thought thatIComparableshould do that. Casting 0 toTValuedoes not work as in 3.bn - 0.5results in a CS0019 error (Operator '-' cannot be applied to operands of type 'TValue' and 'double'), although I thought I could subtract two "numbers".(TValue)0.5results in a CS0030 error (Cannot convert type 'double' to 'TValue'), although I thought that casting a double to a "number" should be possible, after all,doubleimplementsINumber.(binNumber * BinSize)results in a CS0019 error (Operator '*' cannot be applied to operands of type 'int' and 'TValue').(int)Math.Round((value - CorrectValue) / BinSize, MidpointRounding.AwayFromZero)results in CS1503 (Argument 1: cannot convert from TValue to decimal) and CS1503 (Argument 2: cannot convert from System.MidpointRouting to int).
Can't I use the generic INumber as a "number" and do whatever I could do with a float or a double like casting, arithmetic operations +-*/, comparing?
Generic math allows to operate on numbers of the same type (check out the interfaces, they are usually defined using curiously recurring template pattern like
INumber<TSelf> : IComparable<TSelf>), so you need to match types for operations.This can be
bn < TValue.ZeroThis one -
bn - TValue.CreateChecked(0.5)(or otherINumber<TSelf>.CreateXmethods)TValue.CreateCheckedagain(TValue.CreateChecked(binNumber) * BinSize)rounding is defined for
IFloatingPoint<TSelf>-IFloatingPoint<TSelf>.RoundAnd "cast" from the
TValuetointcan be done like this:Note that
CreateCheckedwill throw in case of overflow.