2

I am writing a function that takes a number of any primitive type and decides what the smallest supported type is for that number:

public static Type GetSmallestType( decimal value )
{
  return
    value.CompareTo( sbyte.MinValue ) >= 0 && value.CompareTo( sbyte.MaxValue ) <= 0 ? typeof( sbyte ) :
    value.CompareTo( byte.MinValue ) >= 0 && value.CompareTo( byte.MaxValue ) <= 0 ? typeof( byte ) :
    value.CompareTo( short.MinValue ) >= 0 && value.CompareTo( short.MaxValue ) <= 0 ? typeof( short ) :
    value.CompareTo( ushort.MinValue ) >= 0 && value.CompareTo( ushort.MaxValue ) <= 0 ? typeof( ushort ) :
    value.CompareTo( int.MinValue ) >= 0 && value.CompareTo( int.MaxValue ) <= 0 ? typeof( int ) :
    value.CompareTo( uint.MinValue ) >= 0 && value.CompareTo( uint.MaxValue ) <= 0 ? typeof( uint ) :
    value.CompareTo( long.MinValue ) >= 0 && value.CompareTo( long.MaxValue ) <= 0 ? typeof( long ) :
    value.CompareTo( ulong.MinValue ) >= 0 && value.CompareTo( ulong.MaxValue ) <= 0 ? typeof( ulong ) :
    typeof( decimal );
}

// e.g. GetSmallestType( -10 ) == typeof( sbyte )

This implementation works, but I would like to avoid having an overloaded method for each individual type, as it would result in a lot of duplicate code.

When I try to turn it into a generic function that accepts a generic parameter, it throws an error stating Object must be of type Int32.

public static Type GetSmallestType<T>( T value )
  where T : struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable
{ ... }

The goal is to cast the value to the smallest available type (returned by the method above) and store it in a generic tree node. This question then becomes one of being able to compare all primitive types, whether it be with casting or some other method, as many primitives are not directly comparable.

This is a (likely unnecessary) memory optimization problem which I am attempting for fun.

w0f
  • 908
  • 9
  • 23
  • 1
    Be careful about mixing floating point and whole numbers (integers). – Dennis Kuypers Sep 18 '18 at 17:54
  • 1
    The question is: What does knowing the type allow you to do? Can you state the actual problem? You might be trying to solve a problem or optimize where not needed – Dennis Kuypers Sep 18 '18 at 17:56
  • I don't think this method ever returns `long`/`ulong` as any value in that range also fits in the `float` range - and like @DennisKuypers mentioned, you could store a `long` value in a `float` but lose precision. – C.Evenhuis Sep 18 '18 at 17:58
  • @DennisKuypers Essentially, I am trying to take a given primitive value, and find the smallest type so that I can ultimately convert the value to that type for memory efficiency. Likely not a necessary optimization, but something I am attempting for the challenge and fun of it. Also, this is being designed with whole numbers in mind. I understand the problems that floating points will bring, and will omit them if they become too troublesome. That still leaves the problem of comparing the other primitives, however. – w0f Sep 18 '18 at 17:59
  • The problem is that you will return one of various numeric types and you have to get around static typing - this will most likely outweigh the "optimization" you do. – Dennis Kuypers Sep 18 '18 at 18:01
  • @DennisKuypers That is true, though after deducing the type, I plan on storing it in a generic tree node and as long as that is comparable, then I don't see the need for dynamic typing. Am I missing something with that approach? – w0f Sep 18 '18 at 18:04
  • The generic tree node will be typed to have objects OR you have to have multiple trees, Either will lead to awkward type checking, casting and searching, thus defeating the idea of having a type safe system (somewhat) – Dennis Kuypers Sep 18 '18 at 18:07
  • @DennisKuypers That circles back to the main question then: how would the proper casting and checking be implemented so that all types (except for maybe the floating points) are comparable? – w0f Sep 18 '18 at 18:10
  • If you have a variable that might be in a certain range, store it in a variable that can contain that range. The idea of finding a smaller type for different ranges is not likely to save any memory because you must also store metadata to correctly read that variable. – John Wu Sep 18 '18 at 20:55

1 Answers1

-2

You could cast both as dynamic. Yes, it sort defeats the purpose of using a generic method, but it works if the types are numbers. The struct constraint will help prevent classes from being passed.

It looks like ulong does not work with this method, but everything else does.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(GetSmallestType(100)); // System.SByte
        Console.WriteLine(GetSmallestType(200)); // System.Byte
        Console.WriteLine(GetSmallestType(30_000)); // System.Int16
        Console.WriteLine(GetSmallestType(60_000)); // System.UInt15
        Console.WriteLine(GetSmallestType(100_000)); // System.Int32
        Console.WriteLine(GetSmallestType(4_000_000_000)); // System.UInt23
        Console.WriteLine(GetSmallestType(100_000_000_000)); // System.Int64
        Console.WriteLine(GetSmallestType(20_000_000_000_000_000_000m)); // System.Decimal
    }

    public static Type GetSmallestType<T>(T propertyValue)
        where T : struct
    {
        dynamic value = propertyValue;

        return
            value >= (dynamic)sbyte.MinValue && value <= (dynamic)sbyte.MaxValue ? typeof(sbyte) :
            value >= (dynamic)byte.MinValue && value <= (dynamic)byte.MaxValue ? typeof(byte) :
            value >= (dynamic)short.MinValue && value <= (dynamic)short.MaxValue ? typeof(short) :
            value >= (dynamic)ushort.MinValue && value <= (dynamic)ushort.MaxValue ? typeof(ushort) :
            value >= (dynamic)int.MinValue && value <= (dynamic)int.MaxValue ? typeof(int) :
            value >= (dynamic)uint.MinValue && value <= (dynamic)uint.MaxValue ? typeof(uint) :
            value >= (dynamic)long.MinValue && value <= (dynamic)long.MaxValue ? typeof(long) :
            typeof(decimal);
    }
}
Todd Skelton
  • 6,839
  • 3
  • 36
  • 48