Численная точность реализации для выпуклого четырехугольника

Я реализовал метод вычисления площади выпуклого четырехугольника в R3. Метод работает нормально, но у меня проблемы с числовой точностью в 8-м десятичном знаке. Взгляните на метод:

internal static double GetTriangleArea(double ax, double ay, double az,
            double bx, double by, double bz,
            double cx, double cy, double cz
            )
        {
            /**
             * AB = B-A = (ux, uy, uz)
             * AC = C-A = (wx, wy, wz)
             * 
             * S = 0.5*sqrt{(uy*wz - uz*wy)² + (uz*wx - ux*wz)² + (ux*wy - uy*wx)²}
             * */

            var ux = bx - ax;
            var uy = by - ay;
            var uz = bz - az;

            var wx = cx - ax;
            var wy = cy - ay;
            var wz = cz - az;

            var t1 = uy*wz - uz*wy;
            var t2 = uz*wx - ux*wz;
            var t3 = ux*wy - uy*wx;
            var s = 0.5*Math.Sqrt(t1*t1 + t2*t2 + t3*t3);

            return s;
        }

        internal static double GetConvexQuadrilateralArea(double ax, double ay, double az,
            double bx, double by, double bz,
            double cx, double cy, double cz,
            double dx, double dy, double dz)
        {
            var triangle1 = GetTriangleArea(ax, ay, az, bx, by, bz, cx, cy, cz);
            var triangle2 = GetTriangleArea(ax, ay, az, cx, cy, cz, dx,dy,dz);

            return triangle1 + triangle2;
        }

А это тест:

[TestMethod]
        public void ParallelogramOfBaseBAndHeightHMustHaveAreaEqualToBTimesH()
        {
            var random = new Random(1);
            const double scale = 10000;
            for (var counter = 0; counter < 1000; counter++)
            {
                double baseLength = random.NextDouble() * scale;
                double height = random.NextDouble() * scale;

                double dx = random.NextDouble()*scale;

                var a = new[] { 0, 0, 0 };
                var b = new[] { baseLength, 0, 0 };
                var c = new[] { baseLength+dx, height, 0 };
                var d = new[] { 0F+dx, height, 0 };

                double expected = baseLength * height;

                var result = MathUtils.GetConvexQuadrilateralArea(a[0], a[1], a[2], b[0], b[1], b[2], c[0], c[1], c[2],
                    d[0], d[1], d[2]);

                Assert.AreEqual(expected, result, Epsilon*scale,
                    string.Format("sideA: {0}, height: {1}, dx: {2}", baseLength, height, dx));
            }
        }

Этот тест завершается с ошибкой со следующим сообщением: Ожидаемая разница не более ‹1E-09> между ожидаемым значением 74813926.2967871 и фактическим значением 74813926.2967871. сторона А: 8552.44307245707, высота: 8747.66726454146, дх: 4721.64729829954.

Мой вопрос: есть ли способ повысить числовую точность моей реализации, все еще используя числа с двойной точностью?


person TapiocaCom    schedule 14.02.2014    source источник
comment
Возможно, стоит разместить сообщение на scicomp.stackexchange.com или codereview. stackexchange.com   -  person Patrick Quirk    schedule 14.02.2014
comment
Поскольку результат будет иметь величину масштаба ^ 2, вы можете настроить допуск: Assert.AreEqual(expected, result, Epsilon*scale*scale,...   -  person DasKrümelmonster    schedule 14.02.2014
comment
Просто любопытно, а зачем использовать три значения вместо вектора struct?   -  person John Alexiou    schedule 15.02.2014


Ответы (1)


Двойные числа имеют 52 бита дроби. В базе десять это около 15,7 цифр. Ваши числа имеют восемь цифр с левой стороны от точки, поэтому вы можете ожидать, что только семь цифр будут правильными с правой стороны от точки - и это только из-за представления, даже без учета каких-либо кумулятивных ошибок, вызванных вычислениями. .

Итак, ответ: нет, никак. Вы должны использовать формат с большей точностью.

person jlahd    schedule 14.02.2014