web-dev-qa-db-fra.com

Comment représenter l'infini entier?

J'ai besoin d'un moyen de représenter un nombre entier qui peut être infini. Je préfère ne pas utiliser un type à virgule flottante (double.PositiveInfinity) car le nombre ne peut jamais être fractionnaire et cela pourrait rendre l'API déroutante. Quelle est la meilleure façon de procéder?

Edit: Une idée que je n'ai pas encore vue utilise int? avec null représentant l'infini. Y a-t-il de bonnes raisons de ne pas le faire?

23
ConditionRacer

Si vous n'avez pas besoin de la plage complète de valeurs entières, vous pouvez utiliser le int.MaxValue et int.MinValue constantes pour représenter les infinis.

Cependant, si la gamme complète de valeurs est requise, je suggérerais soit de créer une classe wrapper, soit de choisir simplement des doubles.

24
Smallhacker

Un exemple d'implémentation partielle dans le sens des commentaires de SLaks et autres (feedback bienvenu):

tilisation:

int x = 4;
iint pi = iint.PositiveInfinity;
iint ni = iint.NegativeInfinity;

Assert.IsTrue(x + pi == iint.PositiveInfinity);
Assert.IsTrue(pi + 1 == iint.PositiveInfinity);
Assert.IsTrue(pi + (-ni) == iint.PositiveInfinity);
Assert.IsTrue((int)((iint)5) == 5);

Implémentation:

public struct iint
{
    private readonly int _int;

    public iint(int value) 
    {
        if(value  == int.MaxValue || value == int.MinValue)
            throw new InvalidOperationException("min/max value reserved in iint");
        _int = value;
    }

    public static explicit operator int(iint @this)
    {
        if(@this._int == int.MaxValue || @this._int == int.MinValue)
            throw new InvalidOperationException("cannot implicit convert infinite iint to int");

        return @this._int;
    }

    public static implicit operator iint(int other)
    {
        if(other == int.MaxValue || other == int.MinValue)
            throw new InvalidOperationException("cannot implicit convert max-value into to iint");
        return new iint(other);
    }

    public bool IsPositiveInfinity {get { return _int == int.MaxValue; } }

    public bool IsNegativeInfinity { get { return _int == int.MinValue; } }

    private iint(bool positive)
    {
        if (positive)
            _int = int.MaxValue;
        else
            _int = int.MinValue;
    }

    public static readonly iint PositiveInfinity = new iint(true);

    public static readonly iint NegativeInfinity = new iint(false);

    public static bool operator ==(iint a, iint b)
    {
        return a._int == b._int;
    }

    public static bool operator !=(iint a, iint b)
    {
        return a._int != b._int;
    }

    public static iint operator +(iint a, iint b)
    {
        if (a.IsPositiveInfinity && b.IsNegativeInfinity)
            throw new InvalidOperationException();
        if (b.IsPositiveInfinity && a.IsNegativeInfinity)
            throw new InvalidOperationException();
        if (a.IsPositiveInfinity)
            return PositiveInfinity;
        if (a.IsNegativeInfinity)
            return NegativeInfinity;
        if (b.IsPositiveInfinity)
            return PositiveInfinity;
        if (b.IsNegativeInfinity)
            return NegativeInfinity;

        return a._int + b._int;
    }

    public static iint operator -(iint a, iint b)
    {
        if (a.IsPositiveInfinity && b.IsPositiveInfinity)
            throw new InvalidOperationException();
        if (a.IsNegativeInfinity && b.IsNegativeInfinity)
            throw new InvalidOperationException();
        if (a.IsPositiveInfinity)
            return PositiveInfinity;
        if (a.IsNegativeInfinity)
            return NegativeInfinity;
        if (b.IsPositiveInfinity)
            return NegativeInfinity;
        if (b.IsNegativeInfinity)
            return PositiveInfinity;

        return a._int - b._int;
    }

    public static iint operator -(iint a)
    {
        if (a.IsNegativeInfinity)
            return PositiveInfinity;
        if (a.IsPositiveInfinity)
            return NegativeInfinity;

        return -a;
    }

    /* etc... */
    /* other operators here */
}
4
Jack

Une autre implémentation partielle (je vois que Jack était plus rapide):

struct InfinityInt
{
  readonly int Value;

  InfinityInt(int value, bool allowInfinities)
  {
    if (!allowInfinities && (value == int.MinValue || value == int.MaxValue))
      throw new ArgumentOutOfRangeException("value");
    Value = value;
  }

  public InfinityInt(int value)
    : this(value, false)
  {
  }

  public static InfinityInt PositiveInfinity = new InfinityInt(int.MaxValue, true);

  public static InfinityInt NegativeInfinity = new InfinityInt(int.MinValue, true);

  public bool IsAnInfinity
  {
    get { return Value == int.MaxValue || Value == int.MinValue; }
  }

  public override string ToString()
  {
    if (Value == int.MinValue)
      return double.NegativeInfinity.ToString();
    if (Value == int.MaxValue)
      return double.PositiveInfinity.ToString();

    return Value.ToString();
  }

  public static explicit operator int(InfinityInt ii)
  {
    if (ii.IsAnInfinity)
      throw new OverflowException();
    return ii.Value;
  }
  public static explicit operator double(InfinityInt ii)
  {
    if (ii.Value == int.MinValue)
      return double.NegativeInfinity;
    if (ii.Value == int.MaxValue)
      return double.PositiveInfinity;

    return ii.Value;
  }
  public static explicit operator InfinityInt(int i)
  {
    return new InfinityInt(i); // can throw
  }
  public static explicit operator InfinityInt(double d)
  {
    if (double.IsNaN(d))
      throw new ArgumentException("NaN not supported", "d");
    if (d >= int.MaxValue)
      return PositiveInfinity;
    if (d <= int.MinValue)
      return NegativeInfinity;

    return new InfinityInt((int)d);
  }

  static InfinityInt FromLongSafely(long x)
  {
    if (x >= int.MaxValue)
      return PositiveInfinity;
    if (x <= int.MinValue)
      return NegativeInfinity;

    return new InfinityInt((int)x);
  }

  public static InfinityInt operator +(InfinityInt a, InfinityInt b)
  {
    if (a.IsAnInfinity || b.IsAnInfinity)
    {
      if (!b.IsAnInfinity)
        return a;
      if (!a.IsAnInfinity)
        return b;
      if (a.Value == b.Value)
        return a;

      throw new ArithmeticException("Undefined");
    }
    return FromLongSafely((long)a.Value + (long)b.Value);
  }
  public static InfinityInt operator *(InfinityInt a, InfinityInt b)
  {
    if (a.IsAnInfinity || b.IsAnInfinity)
    {
      if (a.Value == 0 || b.Value == 0)
        throw new ArithmeticException("Undefined");

      return (a.Value > 0) == (b.Value > 0) ? PositiveInfinity : NegativeInfinity;
    }
    return FromLongSafely((long)a.Value * (long)b.Value);
  }

  // and so on, and so on
}
1
Jeppe Stig Nielsen

Votre API peut utiliser une convention qui int.MaxValue représente une valeur infinie positive et int.MinValue - infini négatif.

Mais vous devez toujours le documenter quelque part et, peut-être aurez-vous besoin de quelques opérations avec votre entier infini:

 /// <summary>
/// Making int infinity
/// ...
/// </summary>
public static class IntExtension
{

    public const int PositiveInfinity = int.MaxValue;

    public const int NegativeInfinity = int.MinValue;

    public static bool IsPositiveInfinity(this int x)
    {
        return x == PositiveInfinity;
    }

    public static bool IsNegativeInfinity(this int x)
    {
        return x == NegativeInfinity;
    }

    public static int Operation(this int x, int y)
    {
        // ...

        return PositiveInfinity;
    }
}
1
astef