[libxml2] Parse small XPath numbers more accurately



commit a851868a75c108e3b8fc507f5d6555b09abb51c9
Author: Nick Wellnhofer <wellnhofer aevum de>
Date:   Mon May 29 20:14:42 2017 +0200

    Parse small XPath numbers more accurately
    
    Don't count leading zeros towards the fraction size limit. This allows
    to parse numbers like
    
        0.0000000000000000000000000000000000000000000000000000000001
    
    which is the only standard-conformant way to represent such numbers, as
    scientific notation isn't allowed in XPath 1.0. (It is allowed in XPath
    2.0 and in libxml2 as an extension, though.)
    
    Overall accuracy is still bad, see bug 783238.

 result/XPath/expr/base |    8 ++++++++
 test/XPath/expr/base   |    2 ++
 xpath.c                |   36 ++++++++++++++++--------------------
 3 files changed, 26 insertions(+), 20 deletions(-)
---
diff --git a/result/XPath/expr/base b/result/XPath/expr/base
index e04346f..e2f6389 100644
--- a/result/XPath/expr/base
+++ b/result/XPath/expr/base
@@ -24,5 +24,13 @@ Expression: 1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+
 Object is a number : 21
 
 ========================
+Expression: 0.000000000000000000000000000000000000000000000000001
+Object is a number : 1e-51
+
+========================
+Expression: -0.000000000000000000000000000000000000000000000000001
+Object is a number : -1e-51
+
+========================
 Expression: self::-name
 Object is empty (NULL)
diff --git a/test/XPath/expr/base b/test/XPath/expr/base
index f57e4d0..823f64b 100644
--- a/test/XPath/expr/base
+++ b/test/XPath/expr/base
@@ -4,4 +4,6 @@
 1+2*3+4
 (1+2)*(3+4)
 1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1+1*1
+0.000000000000000000000000000000000000000000000000001
+-0.000000000000000000000000000000000000000000000000001
 self::-name
diff --git a/xpath.c b/xpath.c
index 0a24be6..d40bdda 100644
--- a/xpath.c
+++ b/xpath.c
@@ -10056,20 +10056,6 @@ xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
 
 #define MAX_FRAC 20
 
-/*
- * These are used as divisors for the fractional part of a number.
- * Since the table includes 1.0 (representing '0' fractional digits),
- * it must be dimensioned at MAX_FRAC+1 (bug 133921)
- */
-static double my_pow10[MAX_FRAC+1] = {
-    1.0, 10.0, 100.0, 1000.0, 10000.0,
-    100000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0,
-    10000000000.0, 100000000000.0, 1000000000000.0, 10000000000000.0,
-    100000000000000.0,
-    1000000000000000.0, 10000000000000000.0, 100000000000000000.0,
-    1000000000000000000.0, 10000000000000000000.0, 100000000000000000000.0
-};
-
 /**
  * xmlXPathStringEvalNumber:
  * @str:  A string to scan
@@ -10132,20 +10118,25 @@ xmlXPathStringEvalNumber(const xmlChar *str) {
 #endif
 
     if (*cur == '.') {
-       int v, frac = 0;
+       int v, frac = 0, max;
        double fraction = 0;
 
         cur++;
        if (((*cur < '0') || (*cur > '9')) && (!ok)) {
            return(xmlXPathNAN);
        }
-       while (((*cur >= '0') && (*cur <= '9')) && (frac < MAX_FRAC)) {
+        while (*cur == '0') {
+           frac = frac + 1;
+           cur++;
+        }
+        max = frac + MAX_FRAC;
+       while (((*cur >= '0') && (*cur <= '9')) && (frac < max)) {
            v = (*cur - '0');
            fraction = fraction * 10 + v;
            frac = frac + 1;
            cur++;
        }
-       fraction /= my_pow10[frac];
+       fraction /= pow(10.0, frac);
        ret = ret + fraction;
        while ((*cur >= '0') && (*cur <= '9'))
            cur++;
@@ -10221,20 +10212,25 @@ xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
     }
 #endif
     if (CUR == '.') {
-       int v, frac = 0;
+       int v, frac = 0, max;
        double fraction = 0;
 
         NEXT;
         if (((CUR < '0') || (CUR > '9')) && (!ok)) {
             XP_ERROR(XPATH_NUMBER_ERROR);
         }
-        while ((CUR >= '0') && (CUR <= '9') && (frac < MAX_FRAC)) {
+        while (CUR == '0') {
+            frac = frac + 1;
+            NEXT;
+        }
+        max = frac + MAX_FRAC;
+        while ((CUR >= '0') && (CUR <= '9') && (frac < max)) {
            v = (CUR - '0');
            fraction = fraction * 10 + v;
            frac = frac + 1;
             NEXT;
         }
-        fraction /= my_pow10[frac];
+        fraction /= pow(10.0, frac);
         ret = ret + fraction;
         while ((CUR >= '0') && (CUR <= '9'))
             NEXT;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]