Skip to content

Commit a250ae2

Browse files
committed
Read Float4 values as double and BigDecimal with correct precision
ArrowFlightJdbcFloat4VectorAccessor widened the float to double in getDouble() and passed it to BigDecimal.valueOf(double) in getBigDecimal(), which exposed the binary approximation of the float: 0.1f was returned as 0.10000000149011612. Convert through the float's decimal representation instead, so getDouble(), getObject(Double.class) and getBigDecimal() return the value the float stands for.
1 parent 617edd5 commit a250ae2

2 files changed

Lines changed: 81 additions & 6 deletions

File tree

‎flight/flight-sql-jdbc-core/src/main/java/org/apache/arrow/driver/jdbc/accessor/impl/numeric/ArrowFlightJdbcFloat4VectorAccessor.java‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public float getFloat() {
9999

100100
@Override
101101
public double getDouble() {
102-
return this.getFloat();
102+
return Double.parseDouble(Float.toString(this.getFloat()));
103103
}
104104

105105
@Override
@@ -110,7 +110,7 @@ public BigDecimal getBigDecimal() throws SQLException {
110110
throw new SQLException("BigDecimal doesn't support Infinite/NaN.");
111111
}
112112

113-
return this.wasNull ? null : BigDecimal.valueOf(value);
113+
return this.wasNull ? null : new BigDecimal(Float.toString(value));
114114
}
115115

116116
@Override
@@ -119,7 +119,9 @@ public BigDecimal getBigDecimal(int scale) throws SQLException {
119119
if (Float.isInfinite(value) || Float.isNaN(value)) {
120120
throw new SQLException("BigDecimal doesn't support Infinite/NaN.");
121121
}
122-
return this.wasNull ? null : BigDecimal.valueOf(value).setScale(scale, RoundingMode.HALF_UP);
122+
return this.wasNull
123+
? null
124+
: new BigDecimal(Float.toString(value)).setScale(scale, RoundingMode.HALF_UP);
123125
}
124126

125127
@Override

‎flight/flight-sql-jdbc-core/src/test/java/org/apache/arrow/driver/jdbc/accessor/impl/numeric/ArrowFlightJdbcFloat4VectorAccessorTest.java‎

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,80 @@ public void testShouldGetDoubleMethodFromFloat4Vector() throws Exception {
179179
accessorIterator.assertAccessorGetter(
180180
vector,
181181
ArrowFlightJdbcFloat4VectorAccessor::getDouble,
182-
accessor -> is((double) accessor.getFloat()));
182+
accessor -> is(Double.parseDouble(Float.toString(accessor.getFloat()))));
183+
}
184+
185+
@Test
186+
public void testShouldGetDoubleThatRoundTripsToTheSameFloat() throws Exception {
187+
accessorIterator.iterate(
188+
vector,
189+
(accessor, currentRow) ->
190+
assertThat((float) accessor.getDouble(), is(accessor.getFloat())));
191+
}
192+
193+
@Test
194+
public void testShouldGetDecimalValuesWithoutFloatWideningArtifacts() throws Exception {
195+
final float[] values = {0.1f, -0.1f, 0.2f, 0.3f, 1.1f, 123.456f, 3.4e-5f, -0.0f};
196+
try (final Float4Vector float4Vector =
197+
new Float4Vector("ID", rootAllocatorTestExtension.getRootAllocator())) {
198+
for (int i = 0; i < values.length; i++) {
199+
float4Vector.setSafe(i, values[i]);
200+
}
201+
float4Vector.setValueCount(values.length);
202+
203+
accessorIterator.iterate(
204+
float4Vector,
205+
(accessor, currentRow) -> {
206+
final String expected = Float.toString(values[currentRow]);
207+
assertThat(accessor.getDouble(), is(Double.parseDouble(expected)));
208+
assertThat(accessor.getObject(Double.class), is(Double.parseDouble(expected)));
209+
assertThat(accessor.getBigDecimal(), is(new BigDecimal(expected)));
210+
assertThat(
211+
accessor.getBigDecimal(9),
212+
is(new BigDecimal(expected).setScale(9, RoundingMode.HALF_UP)));
213+
});
214+
}
215+
}
216+
217+
@Test
218+
public void testShouldGetDoubleFromFloat4VectorWithZeroPointOne() throws Exception {
219+
try (final Float4Vector float4Vector =
220+
new Float4Vector("ID", rootAllocatorTestExtension.getRootAllocator())) {
221+
float4Vector.setSafe(0, 0.1f);
222+
float4Vector.setValueCount(1);
223+
224+
accessorIterator.iterate(
225+
float4Vector,
226+
(accessor, currentRow) -> {
227+
assertThat(accessor.getDouble(), is(0.1));
228+
assertThat(accessor.getBigDecimal(), is(new BigDecimal("0.1")));
229+
assertThat(accessor.getBigDecimal(9), is(new BigDecimal("0.100000000")));
230+
});
231+
}
232+
}
233+
234+
@Test
235+
public void testShouldGetDoubleMethodFromFloat4VectorWithNull() throws Exception {
236+
try (final Float4Vector float4Vector =
237+
new Float4Vector("ID", rootAllocatorTestExtension.getRootAllocator())) {
238+
float4Vector.setNull(0);
239+
float4Vector.setValueCount(1);
240+
241+
accessorIterator.assertAccessorGetter(
242+
float4Vector, ArrowFlightJdbcFloat4VectorAccessor::getDouble, is(0.0));
243+
}
244+
}
245+
246+
@Test
247+
public void testShouldGetDoubleMethodFromFloat4VectorWithNaN() throws Exception {
248+
try (final Float4Vector float4Vector =
249+
new Float4Vector("ID", rootAllocatorTestExtension.getRootAllocator())) {
250+
float4Vector.setSafe(0, Float.NaN);
251+
float4Vector.setValueCount(1);
252+
253+
accessorIterator.assertAccessorGetter(
254+
float4Vector, ArrowFlightJdbcFloat4VectorAccessor::getDouble, is(Double.NaN));
255+
}
183256
}
184257

185258
@Test
@@ -191,7 +264,7 @@ public void testShouldGetBigDecimalMethodFromFloat4Vector() throws Exception {
191264
if (Float.isInfinite(value) || Float.isNaN(value)) {
192265
assertThrows(SQLException.class, accessor::getBigDecimal);
193266
} else {
194-
assertThat(accessor.getBigDecimal(), is(BigDecimal.valueOf(value)));
267+
assertThat(accessor.getBigDecimal(), is(new BigDecimal(Float.toString(value))));
195268
}
196269
});
197270
}
@@ -207,7 +280,7 @@ public void testShouldGetBigDecimalWithScaleMethodFromFloat4Vector() throws Exce
207280
} else {
208281
assertThat(
209282
accessor.getBigDecimal(9),
210-
is(BigDecimal.valueOf(value).setScale(9, RoundingMode.HALF_UP)));
283+
is(new BigDecimal(Float.toString(value)).setScale(9, RoundingMode.HALF_UP)));
211284
}
212285
});
213286
}

0 commit comments

Comments
 (0)