Как исправить проблемы с точностью
У меня есть несколько классов, которые следуют той же базовой концепции для работы с юнитами.
Вот простой пример использования одного из них в модульном тесте:
[Test()]
public void Angle_MathOperatorTest()
{
Angle a1 = new Angle(AngleType.Degree, 360);
Angle a2 = new Angle(AngleType.Radian, Math.PI * 2);
Angle addedAngle = a1 + a2;
addedAngle.Degrees.ShouldBeEquivalentTo(720);
Angle subtractedAngle = a1 - a2;
subtractedAngle.Radians.ShouldBeEquivalentTo(0);
}
Я сделал несколько классов, как это продемонстрировал Angle Class, охватывающих другие основные типы юнитов.
Определенным классом, который показал мне, что у меня была проблема точности, было использование класса, который обрабатывает единицы длины: Dimension
Я помог построить базовую библиотеку геометрии, используя этот класс измерений в качестве базового типа единиц измерения. Например, вот класс Point:
public class Point
{
public Dimension X;
public Dimension Y;
public Dimension Z;
}
Линии и другие фигуры имеют такие свойства, как длина, которые представлены измерениями и конечными точками, построенными с использованием этого класса точек.
Проблема становится очевидной, когда я пытаюсь определить, параллельны ли эти линии. Как в этой функции:
/// <summary>
/// checks to see whether every line is parallel
/// </summary>
/// <param name="passedLines">passed List of Lines</param>
/// <returns></returns>
public static bool AreAllParallel(this List<Line> passedLines)
{
for (int i = 0; i < passedLines.Count - 1; i++)
{
if (!passedLines[i].IsParallelTo(passedLines[i + 1]))
{
return false;
}
}
return true;
}
Он часто возвращает false, потому что проверяет слишком высокую точность. После выполнения Поворотов и Переводов с Точками и линиями, округление суммируется настолько, что эта функция возвращает false, когда я хочу, чтобы она возвращала true.
Так:
Какой набор из следующих вариантов является правильным / лучшим выбором?
- просто проверяю такие функции, как
IsParallelTo
для чисел, которые находятся относительно близко (например, в пределах 0,0001 дюйма)
Если (Math.Abs ( thing.x - thing2.x) <.0001)
- совершенствование предыдущей идеи с переменной константой, извлекаемой из файла конфигурации, что позволяет пользователю выбрать желаемое приемлемое отклонение
If(Math.Abs ( thing.x - thing2.x)
- или уменьшение проблемы на корневом уровне в классе измерений:
Я могу использовать ту же стратегию
//inside Dimension Equals
public override bool Equals(object obj)
{
return (Math.Abs(this.Inches - ((Dimension)(obj)).Inches)) < Constants.AcceptedEqualityDeviationConstant;
}
Это бы действительно выглядело так, но вышесказанное легче понять.
return (Math.Abs (this.GetValue(this.InternalUnitType) - ((Dimension)(obj)).GetValue(this.InternalUnitType)))
Или, наконец, моя последняя идея состоит в том, чтобы заменить все в моих классах единиц на базовую единицу Decimal
вместо Double
(Размерность, Угол и т. Д.) До десятичного числа и каким-то образом (после изучения этого) выяснить, поможет ли это.
Как и где я должен улучшить последовательность точности в операциях равенства моих классов?
PS Библиотеки с открытым исходным кодом и могут быть найдены (Единицы здесь и Геометрия здесь)
1 ответ
Если e1
направление первой линии и e2
Затем для направления второй линии, чтобы проверить, параллельны ли они в пределах допустимого отклонения θ (радианы), выполните следующее (псевдокод):
bool is_parallel = |Cross(e1,e2)| <= |e1|*|e2|*Cos(θ)
где |v|
это величина вектора и Cross()
является векторным перекрестным произведением.