Skip to content

Commit d5545fe

Browse files
authored
Merge pull request apache#535 from afs/tdb2-xsd_float
JENA-1674: Don't sign extend Float.floatToIntBits.
2 parents cfd61c0 + efa2a7a commit d5545fe

File tree

6 files changed

+115
-43
lines changed

6 files changed

+115
-43
lines changed

jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/value/DoubleNode62.java

+26-13
Original file line numberDiff line numberDiff line change
@@ -26,37 +26,49 @@
2626
* Uses java's 64 bit long format (which is IEEE754 binary64) except that 2 bits are taken
2727
* from the exponent. This keeps the precision but reduces the range.
2828
* <p>
29-
* <b>Java
30-
* (<a href="https://en.wikipedia.org/wiki/Double-precision_floating-point_format">IEEE
31-
* 754 binary64</a>)</b>
29+
* <b>
30+
* <a href="https://en.wikipedia.org/wiki/Double-precision_floating-point_format">IEEE 754 binary64</a>
31+
* </b>
3232
*
3333
* <pre>
34-
* bit 63 : sign bit
34+
* bit 63 : sign bit
3535
* bits 52-62 : exponent, 11 bits, the power of 2, bias -1023.
3636
* bits 0-51 : mantissa (significand) 52 bits (the leading one is not stored).
3737
*
3838
* Exponents are 11 bits, with values -1022 to +1023 held as 1 to 2046 (11 bits, bias -1023)
39+
* Exponents 0x000 and 0x7ff have a special meaning:
3940
* 0x000 is signed zero.
40-
* 0x7FF is +/- infinity.
41+
* 0x7FF is +/- infinity when the mantissa is zero
42+
* 0x7FF is NaN if the the mantissa is not zero
43+
* The canonical NaN is 0x7FF8000000000000L, i.e. mantissa 0x8000...
44+
* The different NaN values.
4145
* </pre>
4246
*
43-
* for a maximum value of 1.797693e+308 = (2-2^-52)*2^1023 and smallest denormlized of
47+
* The different NaN bit patterns are not distinguishable in Java
48+
* by floating point operations, only by {@link Double#doubleToRawLongBits}.
49+
*
50+
* The maximum value is 1.797693e+308 = (2-2^-52)*2^1023 and smallest denormalized of
4451
* (1-2^-52)*2^-1022 = 2.225...e-308.
4552
* <p>
4653
* <b>DoubleNode62</b>
4754
* <p>
4855
* In a 62 bit double:
49-
*
5056
* <pre>
51-
* bit 63 : pointer bit.
52-
* bit 62 : double type bit.
53-
* bit 61 : sign bit
57+
* <i>NodeId</i>
58+
* bit 63 : pointer or value bit.
59+
* bit 62 : double type bit
60+
*
61+
* <i>Double62</i>
62+
* bit 61 : sign bit
5463
* bits 52-60 : exponent, 9 bits, the power of 2, bias -255
5564
* bits 0-51 : mantissa (significand) 52 bits (the leading one is not stored).
5665
*
5766
* Exponents are 9 bits, with values -254 to 255, held as 1 to 512 (9 bits, bias -255)
67+
* Exponents 0x000 and 0x1ff have a special meaning:
5868
* 0x000 is signed zero.
59-
* 0x1FF is +/- infinity.
69+
* 0x1FF is +/- infinity if the mantissa is zero
70+
* 0x1FF is NaN if the the mantissa is not zero
71+
* The canonical NaN is 0x1FF8000000000000L, i.e. mantissa 0x8000...
6072
* </pre>
6173
*
6274
* for a maximum value of (2-2^-52)*2^255 = 1.157921e+77 and smallest denormlized of
@@ -66,7 +78,7 @@
6678
* <p>
6779
* "No encoding" is 0xFF00_0000_0000_0000L which would otherwise be the smallest (most negative) denormalized value:
6880
* -3.5336941295567687E72
69-
* <p>All unencodeable numbers will endup in the node table in full lexical form.
81+
* <p>All unencodeable numbers will end up in the node table in full lexical form.
7082
*/
7183
public class DoubleNode62 {
7284
/**
@@ -80,7 +92,8 @@ public class DoubleNode62 {
8092
* The top two bits are zero if packing was possible.
8193
*/
8294
public static long pack(double v) {
83-
long x = Double.doubleToRawLongBits(v);
95+
// Not "raw" , so NaNs end up as the same bit pattern when packed.
96+
long x = Double.doubleToLongBits(v);
8497
long sign = BitsLong.unpack(x, 63, 64);
8598
long exp11 = BitsLong.unpack(x, 52, 63);
8699
long exp9 = encode11to9(exp11);

jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/value/FloatNode.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,15 @@
1919
package org.apache.jena.tdb2.store.value;
2020

2121
public class FloatNode {
22+
// Floats,being 32 bits are always encodable.
23+
//public static long NO_ENCODING = 0xFF00_0000_0000_0000L;
24+
2225
// 32 bits of value; collapses NaNs to a single value.
2326

2427
public static long pack(float v) {
25-
return Float.floatToIntBits(v);
28+
// Not "raw" , so NaNs end up as the same bit pattern when packed.
29+
int x = Float.floatToIntBits(v);
30+
return Integer.toUnsignedLong(x);
2631
}
2732

2833
public static float unpack(long v) {

jena-db/jena-tdb2/src/test/java/org/apache/jena/tdb2/store/TS_Store.java

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.apache.jena.dboe.base.block.FileMode;
2222
import org.apache.jena.tdb2.store.value.TestDoubleNode62;
23+
import org.apache.jena.tdb2.store.value.TestFloatNode;
2324
import org.apache.jena.tdb2.store.value.TestNodeIdInline;
2425
import org.apache.jena.tdb2.sys.SystemTDB;
2526
import org.apache.jena.tdb2.sys.TestOps;
@@ -33,6 +34,7 @@
3334
TestNodeId.class
3435
, TestNodeIdInline.class
3536
, TestDoubleNode62.class
37+
, TestFloatNode.class
3638
, TestTripleTable.class
3739
, TestGraphTDB.class
3840
, TestGraphNamedTDB.class

jena-db/jena-tdb2/src/test/java/org/apache/jena/tdb2/store/value/TestDoubleNode62.java

+14-29
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import static org.junit.Assert.*;
2626

2727
public class TestDoubleNode62 {
28+
// See also TestNodeIdInline.nodeId_double_*
2829
@Test public void double_01() { testRoundTripDouble(1d); }
2930
@Test public void double_02() { testRoundTripDouble(-1d); }
3031
@Test public void double_03() { testRoundTripDouble(-1111111111e50d); }
@@ -49,14 +50,18 @@ public class TestDoubleNode62 {
4950
@Test public void double_22() { testRoundTripDouble(Double.NaN); }
5051
@Test public void double_23() { testNoEncoding(Double.MAX_VALUE); }
5152
@Test public void double_24() { testNoEncoding(Double.MIN_NORMAL); }
52-
@Test public void double_25() { testNoEncoding(Double.MIN_VALUE); }
53+
// Despite being out of the normal range of DoubleNode62,
54+
// this does encode and round trip even though out of range.
55+
// Its encoding is long value 1.
56+
@Test public void double_25() { testRoundTripDouble(Double.MIN_VALUE); }
5357

5458
@Test public void double_30() { testRoundTripDouble(DoubleNode62.POSITIVE_INFINITY); }
5559
@Test public void double_31() { testRoundTripDouble(DoubleNode62.NEGATIVE_INFINITY); }
5660
@Test public void double_32() { testRoundTripDouble(DoubleNode62.NaN); }
57-
@Test public void double_33() { testNoEncoding(DoubleNode62.MAX_VALUE); }
58-
@Test public void double_34() { testNoEncoding(DoubleNode62.MIN_NORMAL); }
59-
@Test public void double_35() { testNoEncoding(DoubleNode62.MIN_VALUE); }
61+
62+
@Test public void double_33() { testRoundTripDouble(DoubleNode62.MAX_VALUE); }
63+
@Test public void double_34() { testRoundTripDouble(DoubleNode62.MIN_NORMAL); }
64+
@Test public void double_35() { testRoundTripDouble(DoubleNode62.MIN_VALUE); }
6065

6166
@Test public void double_40() { sameValue(DoubleNode62.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); }
6267
@Test public void double_41() { sameValue(DoubleNode62.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); }
@@ -70,47 +75,27 @@ public class TestDoubleNode62 {
7075
@Test public void double_55() { testConst(DoubleNode62.MIN_VALUE_BITS, 0x01L); }
7176

7277
private void sameValue(double d1, double d2) {
73-
// Not d1 == d2 - NaN != NaN
78+
// Not d1 == d2 because NaN != NaN
7479
assertEquals(Double.valueOf(d1), Double.valueOf(d2));
7580
}
7681

7782
private static void testConst(long x, long expected) {
78-
//print(expected);
79-
//print(x);
8083
assertEquals(expected, x);
8184
double d = DoubleNode62.unpack(x);
8285
long z = DoubleNode62.pack(d);
8386
assertEquals(expected, z);
8487
}
8588

8689
private void testNoEncoding(double d) {
87-
testRoundTripDouble(d, false);
90+
long x = DoubleNode62.pack(d);
91+
assertEquals("Expected no encoding", x, DoubleNode62.NO_ENCODING);
8892
}
8993

9094
private static void testRoundTripDouble(double d) {
91-
testRoundTripDouble(d, true);
92-
}
93-
94-
private static void testRoundTripDouble(double d, boolean valid) {
95-
//System.out.printf("Double: %.2e\n", d);
96-
long x0 = Double.doubleToRawLongBits(d);
97-
//print(x0);
9895
long x = DoubleNode62.pack(d);
99-
//print(x);
100-
if ( x == DoubleNode62.NO_ENCODING ) {
101-
if ( valid )
102-
fail("Expect no encoding");
103-
104-
//System.out.println("No encoding");
105-
//System.out.println();
106-
return;
107-
}
108-
96+
assertNotEquals("Expected encoding", x, DoubleNode62.NO_ENCODING);
10997
double d2 = DoubleNode62.unpack(x);
110-
111-
Double double1 = d ;
112-
Double double2 = d2 ;
113-
assertEquals(double1, double2);
98+
assertEquals(d, d2, 0);
11499
}
115100

116101
private static void print(long x) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.jena.tdb2.store.value;
20+
21+
import static org.junit.Assert.assertEquals;
22+
import static org.junit.Assert.assertTrue;
23+
24+
import org.junit.Test;
25+
26+
public class TestFloatNode {
27+
// Floats can always be encoded.
28+
// See also TestNodeIdInline.nodeId_float_*
29+
@Test public void float_01() { testRoundTripFloat(1f); }
30+
@Test public void float_02() { testRoundTripFloat(-1f); }
31+
@Test public void float_03() { testRoundTripFloat(-1111111111e20f); }
32+
@Test public void float_04() { testRoundTripFloat(1111111111e20f); }
33+
34+
@Test public void float_10() { testRoundTripFloat(Float.POSITIVE_INFINITY); }
35+
@Test public void float_11() { testRoundTripFloat(Float.NEGATIVE_INFINITY); }
36+
@Test public void float_12() { testRoundTripFloat(Float.NaN); }
37+
@Test public void float_13() { testRoundTripFloat(Float.MAX_VALUE); }
38+
@Test public void float_14() { testRoundTripFloat(Float.MIN_NORMAL); }
39+
@Test public void float_15() { testRoundTripFloat(Float.MIN_VALUE); }
40+
41+
private static void testRoundTripFloat(float f) {
42+
long x0 = Float.floatToRawIntBits(f);
43+
long x = FloatNode.pack(f);
44+
// No high part.
45+
assertTrue( (x & 0xFFFFFFFF00000000L) == 0 );
46+
float f2 = FloatNode.unpack(x);
47+
assertEquals(f, f2, 0);
48+
}
49+
}

jena-db/jena-tdb2/src/test/java/org/apache/jena/tdb2/store/value/TestNodeIdInline.java

+18
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,24 @@ public class TestNodeIdInline
257257
@Test public void nodeId_float_5()
258258
{ test("'1.1E9'^^xsd:float") ; }
259259

260+
@Test public void nodeId_float_6()
261+
{ test("'-1'^^xsd:float", "'-1.0'^^xsd:float") ; }
262+
263+
@Test public void nodeId_float_7()
264+
{ test("'-1.0'^^xsd:float") ; }
265+
266+
@Test public void nodeId_float_8()
267+
{ test("'-0.0'^^xsd:float") ; }
268+
269+
@Test public void nodeId_float_9()
270+
{ test("'INF'^^xsd:float") ; }
271+
272+
@Test public void nodeId_float_10()
273+
{ test("'-INF'^^xsd:float") ; }
274+
275+
@Test public void nodeId_float_11()
276+
{ test("'NaN'^^xsd:float") ; }
277+
260278
private void test(String x) { test(x, x) ; }
261279

262280
private void test(String x, String expected) {

0 commit comments

Comments
 (0)