0

I am trying to evaluate a rather long, complicated mathematical expression, and am getting some very weird results out of the python decimal package. (I already tried using regular floats and they don't have sufficient precision). Here is the code (it's a bit long, sorry):

from decimal import *

def docalc (xi0,yi0,xi1,yi1,xi2,yi2,xi3,yi3,xo0,yo0,xo1,yo1,xo2,yo2,xo3,yo3):
    res = (((xi2 * (-yi0 + yi1) + xi1 * (yi0 - yi2) + xi0 * (-yi1 + yi2)) *
          ((xi2 * (-xo0 + xo1) * yi0 * yi1 + xi1 * (xo0 - xo2) * yi0 * yi2 +
            xi0 * (-xo1 + xo2) * yi1 * yi2) *
           (xi2 * xi3 * (xo2 - xo3) * yi0 + xi0 * xi3 * (-xo0 + xo3) * yi2 +
            xi0 * xi2 * (xo0 - xo2) * yi3) -
           (xi1 * xi2 * (-xo1 + xo2) * yi0 + xi0 * xi2 * (xo0 - xo2) * yi1 +
            xi0 * xi1 * (-xo0 + xo1) * yi2) *
           (xi3 * (xo0 - xo2) * yi0 * yi2 + xi2 * (-xo0 + xo3) * yi0 * yi3 +
            xi0 * (xo2 - xo3) * yi2 * yi3)) *
          (xi1 * xi2 * (yi0 - yi3) * (yo1 - yo2) +
           xi0 * (xi1 * (yi2 - yi3) * (yo0 - yo1) - xi2 * (yi1 - yi3) * (yo0 - yo2) +
                  xi3 * (yi1 - yi2) * (yo0 - yo3)) - xi1 * xi3 * (yi0 - yi2) * (yo1 - yo3) +
           xi2 * xi3 * (yi0 - yi1) * (yo2 - yo3)) +
          (-(xi2 * yi0) + xi0 * yi2) * (xi2 * xi3 * (xo2 - xo3) * (yi0 - yi1) +
                                        xi0 * xi3 * (xo0 - xo3) * (yi1 - yi2) +
                                        xi1 * (-(xi3 * (xo1 - xo3) * (yi0 - yi2)) + xi2 * (xo1 - xo2) * (yi0 - yi3) +
                                               xi0 * (xo0 - xo1) * (yi2 - yi3)) - xi0 * xi2 * (xo0 - xo2) * (
                                        yi1 - yi3)) *
          ((-(xi2 * (-xo0 + xo1) * yi0 * yi1) - xi1 * (xo0 - xo2) * yi0 * yi2 -
            xi0 * (-xo1 + xo2) * yi1 * yi2) *
           (xi1 * xi2 * (yi0 - yi3) * (yo1 - yo2) +
            xi0 * (xi1 * (yi2 - yi3) * (yo0 - yo1) - xi2 * (yi1 - yi3) * (yo0 - yo2) +
                   xi3 * (yi1 - yi2) * (yo0 - yo3)) -
            xi1 * xi3 * (yi0 - yi2) * (yo1 - yo3) + xi2 * xi3 * (yi0 - yi1) * (yo2 - yo3))
           - (xi1 * xi2 * (-xo1 + xo2) * yi0 + xi0 * xi2 * (xo0 - xo2) * yi1 +
              xi0 * xi1 * (-xo0 + xo1) * yi2) *
           (xi1 * yi0 * yi2 * yo0 - xi1 * yi0 * yi3 * yo0 + xi3 * yi0 * yi1 * (yo0 - yo1) -
            xi0 * yi1 * yi2 * yo1 + xi0 * yi1 * yi3 * yo1 + xi3 * yi1 * yi2 * (yo1 - yo2) -
            xi1 * yi0 * yi2 * yo2 + xi0 * yi1 * yi2 * yo2 - xi0 * yi2 * yi3 * yo2 +
            xi1 * yi2 * yi3 * yo2 + xi3 * yi0 * yi2 * (-yo0 + yo2) +
            xi2 * yi0 * (yi1 * (-yo0 + yo1) + yi3 * (yo0 - yo3)) + xi1 * yi0 * yi3 * yo3 -
            xi0 * yi1 * yi3 * yo3 + xi0 * yi2 * yi3 * yo3 - xi1 * yi2 * yi3 * yo3 +
            xi2 * yi1 * yi3 * (-yo1 + yo3)))))

    return (res);


def test():

    xi0 = 1.1;      yi0 = 1.1;      xi1 = 2.1;      yi1 = 1.1;      xi2 = 2.1;      yi2 = 2.1;      xi3 = 1.1;      yi3 = 2.1
    xo0 = 91.23;    yo0 = 82.34;    xo1 = 73.45;    yo1 = 64.56;    xo2 = 55.67;    yo2 = 46.78;    xo3 = 37.89;    yo3 = 28.90
    c_float = docalc(xi0, yi0, xi1, yi1, xi2, yi2, xi3, yi3, xo0, yo0, xo1, yo1, xo2, yo2, xo3, yo3)

    getcontext().prec = 19
    xi0d = Decimal(xi0);   yi0d = Decimal(yi0);    xi1d = Decimal(xi1);   yi1d = Decimal(yi1);    xi2d = Decimal(xi2);   yi2d = Decimal(yi2);    xi3d = Decimal(xi3);   yi3d = Decimal(yi3);
    xo0d = Decimal(xo0);   yo0d = Decimal(yo0);    xo1d = Decimal(xo1);   yo1d = Decimal(yo1);    xo2d = Decimal(xo2);   yo2d = Decimal(yo2);    xo3d = Decimal(xo3);   yo3d = Decimal(yo3);
    c_decimal_19 = docalc(xi0d, yi0d, xi1d, yi1d, xi2d, yi2d, xi3d, yi3d, xo0d, yo0d, xo1d, yo1d, xo2d, yo2d, xo3d, yo3d)

    getcontext().prec = 500
    xi0d = Decimal(xi0);   yi0d = Decimal(yi0);    xi1d = Decimal(xi1);   yi1d = Decimal(yi1);    xi2d = Decimal(xi2);   yi2d = Decimal(yi2);    xi3d = Decimal(xi3);   yi3d = Decimal(yi3);
    xo0d = Decimal(xo0);   yo0d = Decimal(yo0);    xo1d = Decimal(xo1);   yo1d = Decimal(yo1);    xo2d = Decimal(xo2);   yo2d = Decimal(yo2);    xo3d = Decimal(xo3);   yo3d = Decimal(yo3);
    c_decimal_500 = docalc(xi0d, yi0d, xi1d, yi1d, xi2d, yi2d, xi3d, yi3d, xo0d, yo0d, xo1d, yo1d, xo2d, yo2d, xo3d, yo3d)

    print("using floats: ",c_float);
    print("using Decimals() w/ prec=19: ",c_decimal_19);
    print("using Decimals() w/ prec=500: ",c_decimal_500);

test();

Here is the output I get:

C:\dev\Python36-32\python.exe C:/dev/PythonProjects/untitled4/main.py
using floats:  -1.1703014024533338e-10
using Decimals() w/ prec=19:  -4.289200000000001210E-13
using Decimals() w/ prec=500:  0E-700

Process finished with exit code 0

For the record, Mathematica says the actual answer is 7.80201*10^-11. I'm not surprised that using floats with such a long complicated expression results in horrendous rounding errors, but I am confused that the Decimal() type, (even with precision set to 500), returns weird spurious results. In fact, increasing the precision from 19 to 500 seems to cause the result to get worse. Any ideas? Is this a bug in the python decimals package, or maybe a bug in python in general with extremely long, convoluted mathematical expressions? Thanks.

mtopinka
  • 127
  • 8
  • 2
    You're making `Decimal`s *from `float`s*, the imprecision is already in there. Try starting with strings instead: `xi0 = '1.1'`. – jonrsharpe Jul 06 '17 at 18:52
  • 1
    You're going to solve problems faster if you look for bugs in your own code before bugs in Python. – user2357112 Jul 06 '17 at 18:56
  • Thanks - sorry about my premature theorizing about possible bugs in python - I'm a little embarrassed now. I had tried pretty hard to figure out what was going on, and had spent a few hours reading and researching and trying things. The solution turned out to be even more forehead-slap-worthy than imprecision from converting between floats and Decimals - it turns out that my long equation in "docalc()" just happened to have a true zero for the random parameters (xi0,yi0,etc) I chose. :( But thank you for the answers - they helped lead me to eventually figuring out the problem. – mtopinka Jul 06 '17 at 21:16

0 Answers0