Создание математической библиотеки с использованием Generics в C #

Существует ли какой-либо возможный способ использования дженериков для создания математической библиотеки, которая не зависит от базового типа, выбранного для хранения данных?

Другими словами, предположим, что я хочу написать class фракций. Дробь может быть представлена ​​двумя целыми или двумя удвоениями или еще что-то. Важно то, что основные четыре арифметические операции хорошо определены. Итак, я хотел бы написать Fraction frac = new Fraction(1,2) и / или Fraction frac = new Fraction(0.1, 1.0) .

К сожалению, нет интерфейса, представляющего четыре основные операции (+, -, *, /). Кто-нибудь нашел эффективный и осуществимый способ реализации этого?

Вот способ абстрагироваться от операторов, которые относительно безболезненны.

  abstract class MathProvider { public abstract T Divide(T a, T b); public abstract T Multiply(T a, T b); public abstract T Add(T a, T b); public abstract T Negate(T a); public virtual T Subtract(T a, T b) { return Add(a, Negate(b)); } } class DoubleMathProvider : MathProvider { public override double Divide(double a, double b) { return a / b; } public override double Multiply(double a, double b) { return a * b; } public override double Add(double a, double b) { return a + b; } public override double Negate(double a) { return -a; } } class IntMathProvider : MathProvider { public override int Divide(int a, int b) { return a / b; } public override int Multiply(int a, int b) { return a * b; } public override int Add(int a, int b) { return a + b; } public override int Negate(int a) { return -a; } } class Fraction { static MathProvider _math; // Notice this is a type constructor. It gets run the first time a // variable of a specific type is declared for use. // Having _math static reduces overhead. static Fraction() { // This part of the code might be cleaner by once // using reflection and finding all the implementors of // MathProvider and assigning the instance by the one that // matches T. if (typeof(T) == typeof(double)) _math = new DoubleMathProvider() as MathProvider; else if (typeof(T) == typeof(int)) _math = new IntMathProvider() as MathProvider; // ... assign other options here. if (_math == null) throw new InvalidOperationException( "Type " + typeof(T).ToString() + " is not supported by Fraction."); } // Immutable impementations are better. public T Numerator { get; private set; } public T Denominator { get; private set; } public Fraction(T numerator, T denominator) { // We would want this to be reduced to simpilest terms. // For that we would need GCD, abs, and remainder operations // defined for each math provider. Numerator = numerator; Denominator = denominator; } public static Fraction operator +(Fraction a, Fraction b) { return new Fraction( _math.Add( _math.Multiply(a.Numerator, b.Denominator), _math.Multiply(b.Numerator, a.Denominator)), _math.Multiply(a.Denominator, b.Denominator)); } public static Fraction operator -(Fraction a, Fraction b) { return new Fraction( _math.Subtract( _math.Multiply(a.Numerator, b.Denominator), _math.Multiply(b.Numerator, a.Denominator)), _math.Multiply(a.Denominator, b.Denominator)); } public static Fraction operator /(Fraction a, Fraction b) { return new Fraction( _math.Multiply(a.Numerator, b.Denominator), _math.Multiply(a.Denominator, b.Numerator)); } // ... other operators would follow. } 

Если вы не реализуете тип, который вы используете, вы получите сбой во время выполнения, а не во время компиляции (это плохо). Определение реализаций MathProvider всегда будет одинаковым (также плохо). Я бы предположил, что вы просто не делаете этого на C # и используете F # или какой-либо другой язык, который лучше подходит для этого уровня абстракции.

Изменить: Исправлены определения добавления и вычитания для Fraction . Еще одна интересная и простая задача – реализовать MathProvider, который работает с абстрактным синтаксическим деревом. Эта идея сразу же указывает на то, что вы делаете, как автоматическая дифференциация: http://conal.net/papers/beautiful-differentiation/

Я считаю, что это отвечает на ваш вопрос:

http://www.codeproject.com/KB/cs/genericnumerics.aspx

Вот тонкая проблема, которая возникает с типичными типами. Предположим, что алгоритм включает деление, например гауссовское устранение, для решения системы уравнений. Если вы передадите целые числа, вы получите неверный ответ, потому что вы будете выполнять целочисленное деление. Но если вы передадите двойные аргументы, которые имеют целые значения, вы получите правильный ответ.

То же самое происходит с квадратными корнями, как в факторизации Холецкого. Факторинг целочисленной матрицы пойдет не так, в то время как факторизация матрицы удвоений, которая имеет целые значения, будет прекрасной.

Во-первых, ваш class должен ограничивать общий параметр примитивами (public class Fraction, где T: struct, new ()).

Во-вторых, вам, вероятно, потребуется создать неявные перегрузки броска, чтобы вы могли обрабатывать кастинг от одного типа к другому без компилятора.

В-третьих, вы можете перегрузить четыре основных оператора, а также сделать интерфейс более гибким при объединении фракций разных типов.

Наконец, вам нужно подумать о том, как вы обрабатываете арифметические операции и переполнения. Хорошая библиотека будет предельно ясна в том, как она обрабатывает переполнения; в противном случае вы не можете доверять результатам операций разных типов фракций.

  • Почему это вычитание не равно нулю?
  • Java округление до int с использованием Math.ceil
  • Поворот группы векторов
  • Является ли двойное умножение разбитым в .NET?
  • Отображение причудливых уравнений с Java
  • Как реализована функция квадратного корня?
  • Почему gdb оценивает sqrt (3) на 0?
  • Как получить целые и дробные части из двух в JSP / Java?
  • Как java вычисляет модуль с отрицательными числами?
  • Невозможно передать значение через перенос
  • Существует ли в Java обычная библиотека рациональных чисел?
  • Давайте будем гением компьютера.