Skip to content

Commit 5b3dc01

Browse files
GuntherRademacherChristianGruen
authored andcommitted
change numeric comparison from double-based to decimal-based
1 parent cf4b8af commit 5b3dc01

File tree

8 files changed

+83
-37
lines changed

8 files changed

+83
-37
lines changed

basex-core/src/main/java/org/basex/query/value/item/ANum.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,38 @@ public final boolean atomicEqual(final Item item) throws QueryException {
8383
*/
8484
protected abstract float flt();
8585

86+
/**
87+
* Converts an item to decimal, if it is untyped, or to double if decimal conversion cannot
88+
* succeed.
89+
* @param item item
90+
* @param ii input info
91+
* @return converted item (unchanged if not untyped, otherwise decimal or double)
92+
* @throws QueryException query exception
93+
*/
94+
protected static Item untypedToDec(final Item item, final InputInfo ii) throws QueryException {
95+
if(!item.type.isUntyped()) return item;
96+
final byte[] token = item.string(ii);
97+
for(final byte b : token) if(b == 'e' || b == 'E' || b == 'N') return Dbl.get(item.dbl(ii));
98+
return Dec.get(item.dec(ii));
99+
}
100+
101+
/**
102+
* Converts an item to float, if it is untyped, or to double if float conversion fails.
103+
* @param item item
104+
* @param ii input info
105+
* @return converted item (unchanged if not untyped, otherwise float or double)
106+
* @throws QueryException query exception
107+
*/
108+
protected static Item untypedToFlt(final Item item, final InputInfo ii) throws QueryException {
109+
if(!item.type.isUntyped()) return item;
110+
try {
111+
return Flt.get(item.flt(ii));
112+
} catch(QueryException ex) {
113+
Util.debug(ex);
114+
return Dbl.get(item.dbl(ii));
115+
}
116+
}
117+
86118
/**
87119
* Returns an absolute value.
88120
* @return absolute value

basex-core/src/main/java/org/basex/query/value/item/Dbl.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,17 @@ public Dbl round(final int prec, final RoundMode mode) {
106106
@Override
107107
public boolean equal(final Item item, final Collation coll, final InputInfo ii)
108108
throws QueryException {
109-
return value == item.dbl(ii);
109+
return !item.type.instanceOf(AtomType.DECIMAL) ? value == item.dbl(ii) :
110+
Double.isFinite(value) ? dec(ii).compareTo(item.dec(ii)) == 0 : false;
110111
}
111112

112113
@Override
113114
public int compare(final Item item, final Collation coll, final boolean transitive,
114115
final InputInfo ii) throws QueryException {
115-
return transitive && item instanceof Dec ? -item.compare(this, coll, transitive, ii) :
116-
compare(value, item.dbl(ii), transitive);
116+
return !item.type.instanceOf(AtomType.DECIMAL) ? compare(value, item.dbl(ii), transitive) :
117+
Double.isFinite(value) ? dec(ii).compareTo(item.dec(ii)) :
118+
value == Double.NEGATIVE_INFINITY ? -1 : value == Double.POSITIVE_INFINITY ? 1 :
119+
transitive ? -1 : NAN_DUMMY;
117120
}
118121

119122
/**

basex-core/src/main/java/org/basex/query/value/item/Dec.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -163,23 +163,24 @@ static BigDecimal round(final BigDecimal value, final int prec, final RoundMode
163163
@Override
164164
public boolean equal(final Item item, final Collation coll, final InputInfo ii)
165165
throws QueryException {
166-
final Type tp = item.type;
167-
return tp.isUntyped() ? dbl() == item.dbl(ii) :
168-
tp == AtomType.DOUBLE || tp == AtomType.FLOAT ? item.equal(this, coll, ii) :
169-
value.compareTo(item.dec(ii)) == 0;
166+
final Item it = untypedToDec(item, ii);
167+
return !(it instanceof Dbl || it instanceof Flt) || Double.isFinite(it.dbl(ii)) ?
168+
value.compareTo(it.dec(ii)) == 0 :
169+
false;
170170
}
171171

172172
@Override
173173
public int compare(final Item item, final Collation coll, final boolean transitive,
174174
final InputInfo ii) throws QueryException {
175-
if(transitive) {
176-
final double n = item.dbl(ii);
177-
if(n == Double.NEGATIVE_INFINITY || Double.isNaN(n)) return 1;
178-
if(n == Double.POSITIVE_INFINITY) return -1;
179-
} else if(item instanceof Dbl || item instanceof Flt) {
180-
return -item.compare(this, coll, transitive, ii);
175+
final Item it = untypedToDec(item, ii);
176+
if(it instanceof Dbl || it instanceof Flt) {
177+
final double n = it.dbl(ii);
178+
if(!Double.isFinite(n)) {
179+
return n == Double.NEGATIVE_INFINITY ? 1 : n == Double.POSITIVE_INFINITY ? -1 :
180+
transitive ? 1 : NAN_DUMMY;
181+
}
181182
}
182-
return value.compareTo(item.dec(ii));
183+
return value.compareTo(it.dec(ii));
183184
}
184185

185186
@Override

basex-core/src/main/java/org/basex/query/value/item/Flt.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,22 @@ public Flt round(final int prec, final RoundMode mode) {
107107
@Override
108108
public boolean equal(final Item item, final Collation coll, final InputInfo ii)
109109
throws QueryException {
110-
return item.type == AtomType.DOUBLE ? item.equal(this, coll, ii) : value == item.flt(ii);
110+
final Item it = untypedToFlt(item, ii);
111+
return it instanceof Flt flt ? value == flt.value :
112+
it instanceof Dbl ? it.equal(this, coll, ii) :
113+
Float.isFinite(value) ? dec(ii).compareTo(it.dec(ii)) == 0 : false;
111114
}
112115

113116
@Override
114117
public int compare(final Item item, final Collation coll, final boolean transitive,
115118
final InputInfo ii) throws QueryException {
116-
return item instanceof Dbl || transitive && item instanceof Dec
117-
? -item.compare(this, coll, transitive, ii) : compare(value, item.flt(ii), transitive);
119+
final Item it = untypedToFlt(item, ii);
120+
return it instanceof Flt flt ? compare(value, flt.value, transitive) :
121+
it instanceof Dbl ? -it.compare(this, coll, transitive, ii) :
122+
Float.isFinite(value) ? dec(ii).compareTo(it.dec(ii)) :
123+
value == Float.NEGATIVE_INFINITY ? -1 :
124+
value == Float.POSITIVE_INFINITY ? 1 :
125+
transitive ? -1 : NAN_DUMMY;
118126
}
119127

120128
/**

basex-core/src/main/java/org/basex/query/value/item/Itr.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,16 +151,14 @@ public int toInt() {
151151
public boolean equal(final Item item, final Collation coll, final InputInfo ii)
152152
throws QueryException {
153153
return item instanceof final Itr itr ? value == itr.value :
154-
item instanceof Dec ? item.equal(this, coll, ii) :
155-
value == item.dbl(ii);
154+
untypedToDec(item, ii).equal(this, coll, ii);
156155
}
157156

158157
@Override
159158
public int compare(final Item item, final Collation coll, final boolean transitive,
160159
final InputInfo ii) throws QueryException {
161160
return item instanceof final Itr itr ? Long.compare(value, itr.value) :
162-
item instanceof Dec ? -item.compare(this, coll, transitive, ii) :
163-
Dbl.compare(dbl(ii), item.dbl(ii), transitive);
161+
-untypedToDec(item, ii).compare(this, coll, transitive, ii);
164162
}
165163

166164
@Override

basex-core/src/main/java/org/basex/query/value/item/Uln.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,17 @@ public ANum round(final int prec, final RoundMode mode) {
8888
@Override
8989
public boolean equal(final Item item, final Collation coll, final InputInfo ii)
9090
throws QueryException {
91-
return item.type == AtomType.UNSIGNED_LONG ? value.equals(((Uln) item).value) :
92-
item.type == AtomType.DOUBLE || item.type == AtomType.FLOAT || item.type == AtomType.DECIMAL ?
93-
item.equal(this, coll, ii) :
94-
value.compareTo(BigInteger.valueOf(item.itr(ii))) == 0;
91+
return item instanceof final Uln uln ? value.equals(uln.value) :
92+
item instanceof Itr ? value.equals(BigInteger.valueOf(item.itr(ii))) :
93+
untypedToDec(item, ii).equal(this, coll, ii);
9594
}
9695

9796
@Override
9897
public int compare(final Item item, final Collation coll, final boolean transitive,
9998
final InputInfo ii) throws QueryException {
100-
return item.type == AtomType.UNSIGNED_LONG ? value.compareTo(((Uln) item).value) :
101-
item.type == AtomType.DOUBLE || item.type == AtomType.FLOAT || item.type == AtomType.DECIMAL ?
102-
-item.compare(this, coll, transitive, ii) :
103-
value.compareTo(BigInteger.valueOf(item.itr(ii)));
99+
return item instanceof final Uln uln ? value.compareTo(uln.value) :
100+
item instanceof Itr ? value.compareTo(BigInteger.valueOf(item.itr(ii))) :
101+
-untypedToDec(item, ii).compare(this, coll, transitive, ii);
104102
}
105103

106104
@Override

basex-core/src/test/java/org/basex/query/ast/RewritingsTest.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,11 @@ public final class RewritingsTest extends SandboxTest {
171171
check("<a>5</a>[text() <= -800000000]", "", exists(cmpr));
172172
check("<a>5</a>[text() > 8000000000000000000]", "", exists(cmpr));
173173
check("<a>5</a>[text() < -8000000000000000000]", "", exists(cmpr));
174-
check("exists(<x>1234567890.12345678</x>[. = 1234567890.1234567])", true, empty(cmpr));
174+
check("exists(<x>1234567890.12345678</x>[. = 1234567890.1234567])", false, empty(cmpr));
175+
check("exists(<x>1234567890.12345678e0</x>[. = 1234567890.1234567e0])", true, empty(cmpr));
175176

176-
check("exists(<x>123456789012345678</x> [. = 123456789012345679])", true, empty(cmpr));
177+
check("exists(<x>123456789012345678</x> [. = 123456789012345679])", false, empty(cmpr));
178+
check("exists(<x>123456789012345678e0</x> [. = 123456789012345679e0])", true, empty(cmpr));
177179
check("<a>5</a>[xs:integer(.) > 8000000000000000000]", "", empty(cmpr));
178180
check("<a>5</a>[xs:integer(.) < -8000000000000000000]", "", empty(cmpr));
179181
check("(1, 1234567890.12345678)[. = 1234567890.1234567]", "", empty(cmpr));
@@ -885,9 +887,13 @@ public final class RewritingsTest extends SandboxTest {
885887
"2\n3\n4\n5\n6\n7", empty(Cast.class));
886888

887889
check("for $n in (10000000000000000, 1)[. != 0] return number($n) = 10000000000000001",
888-
"true\nfalse", exists(NUMBER));
890+
"false\nfalse", exists(NUMBER));
891+
check("for $n in (10000000000000000, 1)[. != 0] return number($n) = "
892+
+ "xs:double(10000000000000001)", "true\nfalse", exists(NUMBER));
889893
check("for $n in (10000000000000000, 1)[. != 0] return xs:double($n) = 10000000000000001",
890-
"true\nfalse", exists(Cast.class));
894+
"false\nfalse", exists(Cast.class));
895+
check("for $n in (10000000000000000, 1)[. != 0] return xs:double($n) = "
896+
+ "xs:double(10000000000000001)", "true\nfalse", exists(Cast.class));
891897

892898
check("number(<?_ 1?>) + number(<_>2</_>)", 3, count(NUMBER, 1));
893899
check("xs:double(<?_ 1?>) + xs:double(<_>2</_>)", 3, count(Cast.class, 1));

basex-core/src/test/java/org/basex/query/simple/SimpleTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ public final class SimpleTest extends QueryTest {
5353
{ "Compare 5", booleans(false), "1234567890.12345678 = 1234567890.1234567" },
5454
{ "Compare 6", booleans(false), "123456789012345678 = 123456789012345679" },
5555
// GH-2112, GH-2115
56-
{ "Compare 7", booleans(false), "xs:decimal(1.13) gt xs:double(1.13)" },
57-
{ "Compare 8", booleans(false), "xs:decimal(1.13) gt xs:float(1.13)" },
58-
{ "Compare 9", booleans(true), "xs:decimal(1.13) le xs:double(1.13)" },
59-
{ "Compare 10", booleans(true), "xs:decimal(1.13) le xs:float(1.13)" },
56+
{ "Compare 7", booleans(true), "xs:decimal(1.13) gt xs:double(1.13)" },
57+
{ "Compare 8", booleans(true), "xs:decimal(1.13) gt xs:float(1.13)" },
58+
{ "Compare 9", booleans(false), "xs:decimal(1.13) le xs:double(1.13)" },
59+
{ "Compare 10", booleans(false), "xs:decimal(1.13) le xs:float(1.13)" },
6060
// GH-2113, GH-2114
6161
{ "Compare 11", booleans(false), "xs:float (1.13) ge xs:double(1.13)" },
6262
{ "Compare 12", booleans(true), "xs:float (1.13) le xs:double(1.13)" },

0 commit comments

Comments
 (0)