From 9e4b9d6f5ed680d8c269c97641fae5ad68c5c9c2 Mon Sep 17 00:00:00 2001 From: Eduard Braun Date: Wed, 31 Aug 2016 00:04:19 +0200 Subject: [PATCH] Improve behaviour of numerial precision reduction * Previously all calculations were immediately done with the precision specified by the user, resulting in accumulation of numerical errors and in some cases very discernible abberations from the initial image (e.g. #80) * Now all calculations are done with default precision (the module "decimal" uses a whopping 28 signifigant digits initially!) and only at the very end coordinates are rounded to the desired precision. --- scour/scour.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/scour/scour.py b/scour/scour.py index 31cf1e7..17168ef 100644 --- a/scour/scour.py +++ b/scour/scour.py @@ -64,12 +64,7 @@ import optparse from scour.yocto_css import parseCssString import six from six.moves import range - -# Python 2.3- did not have Decimal -try: - from decimal import Decimal, InvalidOperation, getcontext -except ImportError: - sys.stderr.write("Scour requires at least Python 2.7 or Python 3.3+.") +from decimal import Context, Decimal, InvalidOperation, getcontext # select the most precise walltime measurement function available on the platform if sys.platform.startswith('win'): @@ -2474,9 +2469,13 @@ def scourUnitlessLength(length, needsRendererWorkaround=False): # length is of a This is faster than scourLength on elements guaranteed not to contain units. """ - # reduce to the proper number of digits if not isinstance(length, Decimal): length = getcontext().create_decimal(str(length)) + + # reduce numeric precision + # plus() corresponds to the unary prefix plus operator and applies context precision and rounding + length = scouringContext.plus(length) + # if the value is an integer, it may still have .0[...] attached to it for some reason # remove those if int(length) == length: @@ -3068,7 +3067,11 @@ def scourString(in_string, options=None): # sanitize options (take missing attributes from defaults, discard unknown attributes) options = sanitizeOptions(options) - getcontext().prec = options.digits + # create decimal context with reduced precision for scouring numbers + # calculations should be done in the default context (precision defaults to 28 significant digits) to minimize errors + global scouringContext + scouringContext = Context(prec = options.digits) + global numAttrsRemoved global numStylePropsFixed global numElemsRemoved