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 < 0
results in a CS0019 error (Operator '<' cannot be applied to operands of type 'TValue' and 'int'), although I thought that IComparable
should do that. Casting 0 to TValue
does not work as in 3.
bn - 0.5
results 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.5
results 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, double
implements INumber
.
(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.
bn < 0
results in a CS0019 error (Operator '<' cannot be applied to operands of type 'TValue' and 'int'), although I thought that IComparable should do that
This can be bn < TValue.Zero
bn - 0.5
results in a CS0019 error (Operator '-' cannot be applied to operands of type 'TValue' and 'double'), although I thought I could subtract two "numbers".
This one - bn - TValue.CreateChecked(0.5)
(or other INumber<TSelf>.CreateX
methods)
(TValue)0.5
TValue.CreateChecked
again
(binNumber * BinSize)
(TValue.CreateChecked(binNumber) * BinSize)
(int)Math.Round((value - CorrectValue) / BinSize, MidpointRounding.AwayFromZero)
rounding is defined for IFloatingPoint<TSelf>
- IFloatingPoint<TSelf>.Round
And "cast" from the TValue
to int
can be done like this:
int.CreateChecked(binNumber)
Note that CreateChecked
will throw in case of overflow.