You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2018/12/21 15:18:25 UTC
[sis] 02/02: GridChange should round to the enclosing GridExtent
instead than nearest. We added a GridRoundingMode enumeration for giving
control.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
commit 2020033357e5f68cb3519f33ccc55b1c5b20028a
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Fri Dec 21 16:17:17 2018 +0100
GridChange should round to the enclosing GridExtent instead than nearest. We added a GridRoundingMode enumeration for giving control.
---
.../org/apache/sis/coverage/grid/GridChange.java | 44 +++++++++++-
.../org/apache/sis/coverage/grid/GridExtent.java | 82 +++++++++++++++-------
.../org/apache/sis/coverage/grid/GridGeometry.java | 13 ++--
.../apache/sis/coverage/grid/GridRoundingMode.java | 64 +++++++++++++++++
.../apache/sis/coverage/grid/GridExtentTest.java | 8 +--
.../apache/sis/coverage/grid/GridGeometryTest.java | 4 +-
.../java/org/apache/sis/math/SequenceVector.java | 12 ++--
.../src/main/java/org/apache/sis/math/Vector.java | 6 +-
8 files changed, 186 insertions(+), 47 deletions(-)
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java
index 4566fea..1046fe9 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java
@@ -108,6 +108,8 @@ public class GridChange implements Serializable {
* <li>Target {@linkplain GridGeometry#getExtent() extent}.</li>
* </ul>
*
+ * This constructor assumes {@link GridRoundingMode#ENCLOSING} and no margin.
+ *
* @param source the source grid geometry.
* @param target the target grid geometry.
* @throws IncompleteGridGeometryException if a "grid to CRS" conversion is not defined,
@@ -115,8 +117,44 @@ public class GridChange implements Serializable {
* @throws TransformException if an error occurred during conversion from source to target.
*/
public GridChange(final GridGeometry source, final GridGeometry target) throws TransformException {
- ArgumentChecks.ensureNonNull("source", source);
- ArgumentChecks.ensureNonNull("target", target);
+ this(source, target, GridRoundingMode.ENCLOSING, null);
+ }
+
+ /**
+ * Creates a description of the conversion from given source grid geometry to given target grid geometry.
+ * The following information are mandatory:
+ * <ul>
+ * <li>Source {@linkplain GridGeometry#getExtent() extent}.</li>
+ * <li>Source "{@linkplain GridGeometry#getGridToCRS(PixelInCell) grid to CRS}" conversion.</li>
+ * <li>Target "grid to CRS" conversion.</li>
+ * </ul>
+ *
+ * The following information are optional but recommended:
+ * <ul>
+ * <li>Source coordinate reference system.</li>
+ * <li>Target {@linkplain GridGeometry#getCoordinateReferenceSystem() coordinate reference system}.</li>
+ * <li>Target {@linkplain GridGeometry#getExtent() extent}.</li>
+ * </ul>
+ *
+ * An optional {@code margin} can be specified for increasing the size of the grid extent computed by this constructor.
+ * For example if the caller wants to apply bilinear interpolations in an image, it will needs 1 more pixel on each
+ * image border. If the caller wants to apply bi-cubic interpolations, it will needs 2 more pixels on each image border.
+ * If the {@code margin} array length is shorter than the target dimension, then zero is assumed for all missing dimensions.
+ *
+ * @param source the source grid geometry.
+ * @param target the target grid geometry.
+ * @param rounding controls behavior of rounding from floating point values to integers.
+ * @param margin if non-null, expand the extent by that amount of cells on each target dimension.
+ * @throws IncompleteGridGeometryException if a "grid to CRS" conversion is not defined,
+ * or if the {@code source} does not specify an {@linkplain GridGeometry#getExtent() extent}.
+ * @throws TransformException if an error occurred during conversion from source to target.
+ */
+ public GridChange(final GridGeometry source, final GridGeometry target, final GridRoundingMode rounding, final int... margin)
+ throws TransformException
+ {
+ ArgumentChecks.ensureNonNull("source", source);
+ ArgumentChecks.ensureNonNull("target", target);
+ ArgumentChecks.ensureNonNull("rounding", rounding);
if (source.equals(target)) {
// Optimization for a common case.
mapCorners = mapCenters = MathTransforms.identity(source.getDimension());
@@ -131,7 +169,7 @@ public class GridChange implements Serializable {
final GridExtent domain = source.getExtent();
mapCorners = path(source, crsChange, target, PixelInCell.CELL_CORNER);
mapCenters = path(source, crsChange, target, PixelInCell.CELL_CENTER);
- targetRange = new GridExtent(domain.toCRS(mapCorners, mapCenters), target.extent, null);
+ targetRange = new GridExtent(domain.toCRS(mapCorners, mapCenters), rounding, margin, target.extent, null);
/*
* Get an estimation of the scale factors when converting from source to target.
* If all scale factors are 1, we will not store the array for consistency with
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
index 0a5b984..e565d17 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
@@ -290,46 +290,78 @@ public class GridExtent implements Serializable {
* <p><b>NOTE:</b> this constructor is not public because its contract is a bit approximate.</p>
*
* @param envelope the envelope containing cell indices to store in a {@code GridExtent}.
+ * @param rounding controls behavior of rounding from floating point values to integers.
+ * @param margin if non-null, expand the extent by that amount of cells on each envelope dimension.
* @param enclosing if the new grid is a sub-grid of a larger grid, that larger grid. Otherwise {@code null}.
* @param modifiedDimensions if {@code enclosing} is non-null, the grid dimensions to set from the envelope.
* The length of this array shall be equal to the {@code envelope} dimension.
*
* @see #toCRS(MathTransform, MathTransform)
*/
- GridExtent(final AbstractEnvelope envelope, final GridExtent enclosing, final int[] modifiedDimensions) {
+ GridExtent(final AbstractEnvelope envelope, final GridRoundingMode rounding, final int[] margin,
+ final GridExtent enclosing, final int[] modifiedDimensions)
+ {
final int dimension = envelope.getDimension();
coordinates = (enclosing != null) ? enclosing.coordinates.clone() : allocate(dimension);
for (int i=0; i<dimension; i++) {
final double min = envelope.getLower(i);
final double max = envelope.getUpper(i);
if (min >= Long.MIN_VALUE && max <= Long.MAX_VALUE && min <= max) {
- long lower = Math.round(min);
- long upper = Math.round(max);
- if (lower != upper) upper--; // For making the coordinate inclusive.
- /*
- * The [lower … upper] range may be slightly larger than desired in some rounding error situations.
- * For example if 'min' was 1.49999 and 'max' was 2.50001, the roundings will create a [1…3] range
- * while there is actually only 2 pixels. We detect those rounding problems by comparing the spans
- * before and after rounding. We attempt an adjustment only if the span mistmatch is ±1, otherwise
- * the difference is assumed to be caused by overflow. On the three values that can be affected by
- * the adjustment (min, max and span), we change only the number which is farthest from an integer
- * value.
- */
- long error = (upper - lower) + 1; // Negative number if overflow.
- if (error >= 0) {
- final double span = envelope.getSpan(i);
- final long extent = Math.round(span);
- if (extent != 0 && Math.abs(error -= extent) == 1) {
- final double dmin = Math.abs(min - Math.rint(min));
- final double dmax = Math.abs(max - Math.rint(max));
- final boolean adjustMax = (dmax >= dmin);
- if (Math.abs(span - extent) < (adjustMax ? dmax : dmin)) {
- if (adjustMax) upper = Math.subtractExact(upper, error);
- else lower = Math.addExact(lower, error);
+ long lower, upper;
+ switch (rounding) {
+ default: {
+ throw new AssertionError(rounding);
+ }
+ case ENCLOSING: {
+ lower = (long) Math.floor(min);
+ upper = (long) Math.ceil (max);
+ if (lower != upper) upper--; // For making the coordinate inclusive.
+ break;
+ }
+ case NEAREST: {
+ lower = Math.round(min);
+ upper = Math.round(max);
+ if (lower != upper) upper--; // For making the coordinate inclusive.
+ /*
+ * The [lower … upper] range may be slightly larger than desired in some rounding error situations.
+ * For example if 'min' was 1.49999 and 'max' was 2.50001, the rounding will create a [1…3] range
+ * while there is actually only 2 pixels. We detect those rounding problems by comparing the spans
+ * before and after rounding. We attempt an adjustment only if the span mismatch is ±1, otherwise
+ * the difference is assumed to be caused by overflow. On the three values that can be affected by
+ * the adjustment (min, max and span), we change only the number which is farthest from an integer
+ * value.
+ */
+ long error = (upper - lower) + 1; // Negative number if overflow.
+ if (error >= 0) {
+ final double span = envelope.getSpan(i);
+ final long extent = Math.round(span);
+ if (extent != 0 && Math.abs(error -= extent) == 1) {
+ final double dmin = Math.abs(min - Math.rint(min));
+ final double dmax = Math.abs(max - Math.rint(max));
+ final boolean adjustMax = (dmax >= dmin);
+ if (Math.abs(span - extent) < (adjustMax ? dmax : dmin)) {
+ if (adjustMax) upper = Math.subtractExact(upper, error);
+ else lower = Math.addExact(lower, error);
+ }
+ }
}
}
}
/*
+ * If the user specified a margin, add it now. The margin dimension indices follow the envelope
+ * dimension indices. Note that the resulting extent will be intersected with enclosing extent
+ * at the next step, which may cancel the margin effect.
+ */
+ if (margin != null && i < margin.length) {
+ final int m = margin[i];
+ lower = Math.subtractExact(lower, m);
+ upper = Math.addExact(upper, m);
+ }
+ if (lower > upper) {
+ upper += (lower - upper) >>> 1; // (upper - lower) as unsigned integer: overflow-safe.
+ lower = upper;
+ }
+ /*
* At this point the grid range has been computed (lower to upper).
* Update the coordinates accordingly.
*/
@@ -559,7 +591,7 @@ public class GridExtent implements Serializable {
* If different, then this is assumed to map pixel centers instead than pixel corners.
* @return this grid extent in real world coordinates.
*
- * @see #GridExtent(AbstractEnvelope, GridExtent, int[])
+ * @see #GridExtent(AbstractEnvelope, GridRoundingMode, int[], GridExtent, int[])
*/
final GeneralEnvelope toCRS(final MathTransform cornerToCRS, final MathTransform gridToCRS) throws TransformException {
final int dimension = getDimension();
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
index ce7db8a..cccd6dc 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
@@ -354,7 +354,7 @@ public class GridGeometry implements Serializable {
* coordinates used in {@link GridExtent}.
* The rules for deciding whether coordinates should be rounded toward nearest integers,
* to {@linkplain Math#floor(double) floor} or to {@linkplain Math#ceil(double) ceil} values
- * are implementation details and may be adjusted in any Apache SIS versions.</p>
+ * are specified by the {@link GridRoundingMode} argument.</p>
*
* <p>Because of the uncertainties explained in above paragraph, this constructor should be used only in last resort,
* when the grid extent is unknown. For determinist results, developers should prefer the
@@ -373,22 +373,26 @@ public class GridGeometry implements Serializable {
* @param envelope the geospatial envelope, including its coordinate reference system if available.
* There is no guarantees that the envelope actually stored in the {@code GridGeometry}
* will be equal to this specified envelope.
+ * @param rounding controls behavior of rounding from floating point values to integers.
* @throws TransformException if the math transform can not compute the grid extent or the resolution.
*/
@SuppressWarnings("null")
- public GridGeometry(final PixelInCell anchor, final MathTransform gridToCRS, final Envelope envelope) throws TransformException {
+ public GridGeometry(final PixelInCell anchor, final MathTransform gridToCRS, final Envelope envelope,
+ final GridRoundingMode rounding) throws TransformException
+ {
if (gridToCRS == null) {
ArgumentChecks.ensureNonNull("envelope", envelope);
} else if (envelope != null) {
ensureDimensionMatches("envelope", gridToCRS.getTargetDimensions(), envelope.getDimension());
}
+ ArgumentChecks.ensureNonNull("rounding", rounding);
this.gridToCRS = PixelTranslation.translate(gridToCRS, anchor, PixelInCell.CELL_CENTER);
this.cornerToCRS = PixelTranslation.translate(gridToCRS, anchor, PixelInCell.CELL_CORNER);
Matrix scales = MathTransforms.getMatrix(gridToCRS);
int numToIgnore = 1;
if (envelope != null && cornerToCRS != null) {
GeneralEnvelope env = Envelopes.transform(cornerToCRS.inverse(), envelope);
- extent = new GridExtent(env, null, null);
+ extent = new GridExtent(env, GridRoundingMode.NEAREST, null, null, null);
env = extent.toCRS(cornerToCRS, gridToCRS); // 'gridToCRS' specified by the user, not 'this.gridToCRS'.
env.setCoordinateReferenceSystem(envelope.getCoordinateReferenceSystem());
this.envelope = new ImmutableEnvelope(env);
@@ -607,7 +611,8 @@ public class GridGeometry implements Serializable {
} catch (FactoryException e) {
throw new TransformException(Resources.format(Resources.Keys.CanNotMapToGridDimensions), e);
}
- final GridExtent sub = new GridExtent(Envelopes.transform(gridToAOI.inverse(), areaOfInterest), extent, modifiedDimensions);
+ final GridExtent sub = new GridExtent(Envelopes.transform(gridToAOI.inverse(), areaOfInterest),
+ GridRoundingMode.NEAREST, null, extent, modifiedDimensions);
return sub.equals(extent) ? extent : sub;
}
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridRoundingMode.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridRoundingMode.java
new file mode 100644
index 0000000..529f43d
--- /dev/null
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridRoundingMode.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.coverage.grid;
+
+
+/**
+ * Specifies rounding behavior of computations of {@link GridExtent} from floating-point values.
+ * The rounding mode controls how real numbers are converted into {@code GridExtent}'s
+ * {@linkplain GridExtent#getLow(int) low}, {@linkplain GridExtent#getHigh(int) high} and
+ * {@linkplain GridExtent#getSize(int) size} integer values.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+public enum GridRoundingMode {
+ /**
+ * Converts grid low, high and size to nearest integer values.
+ * This mode applies the following steps:
+ *
+ * <ol>
+ * <li>Floating point values are converted to integers with {@link Math#round(double)}.</li>
+ * <li>On the three integer values ({@linkplain GridExtent#getLow(int) low}, {@linkplain GridExtent#getHigh(int) high}
+ * and {@linkplain GridExtent#getSize(int) size}) obtained after rounding, add or subtract 1 to the value which is
+ * farthest from an integer value in order to keep unchanged the two values that are closer to integers.</li>
+ * </ol>
+ *
+ * <div class="note"><b>Example:</b>
+ * the [<var>low</var> … <var>high</var>] range may be slightly larger than desired in some rounding error situations.
+ * For example if <var>low</var> before rounding was 1.49999 and <var>high</var> before rounding was 2.50001, then the
+ * range after rounding will be [1…3] while the expected size is actually only 2 pixels. This {@code NEAREST} rounding
+ * mode detects those rounding issues by comparing the <var>size</var> before and after rounding. In this example, the
+ * size is 2.00002 pixels, which is closer to an integer value than the <var>low</var> and <var>high</var> values.
+ * Consequently this {@code NEAREST} mode will rather adjust <var>low</var> or <var>high</var> (depending which one is
+ * farthest from integer values) in order to keep <var>size</var> at its closest integer value, which is 2.</div>
+ */
+ NEAREST,
+
+ /**
+ * Converts grid low and high to values that fully encloses the envelope.
+ * This mode applies the following steps:
+ *
+ * <ul>
+ * <li>Grid {@linkplain GridExtent#getLow(int) low} are converted to integers with {@link Math#floor(double)}.</li>
+ * <li>Grid {@linkplain GridExtent#getHigh(int) high} are converted to integers with {@link Math#ceil(double)}.</li>
+ * </ul>
+ */
+ ENCLOSING
+}
diff --git a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
index 068d993..3519a8d 100644
--- a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
+++ b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
@@ -46,14 +46,14 @@ public final strictfp class GridExtentTest extends TestCase {
}
/**
- * Tests the {@link GridExtent#GridExtent(AbstractEnvelope, GridExtent, int[])} constructor.
+ * Tests the {@link GridExtent#GridExtent(AbstractEnvelope, GridRoundingMode, int[], GridExtent, int[])} constructor.
*/
@Test
public void testCreateFromEnvelope() {
final GeneralEnvelope env = new GeneralEnvelope(HardCodedCRS.IMAGE);
env.setRange(0, -23.01, 30.107);
env.setRange(1, 12.97, 18.071);
- GridExtent extent = new GridExtent(env, null, null);
+ GridExtent extent = new GridExtent(env, GridRoundingMode.NEAREST, null, null, null);
assertExtentEquals(extent, 0, -23, 29);
assertExtentEquals(extent, 1, 13, 17);
assertEquals(DimensionNameType.COLUMN, extent.getAxisType(0).get());
@@ -61,7 +61,7 @@ public final strictfp class GridExtentTest extends TestCase {
}
/**
- * Tests the rounding performed by the {@link GridExtent#GridExtent(AbstractEnvelope, GridExtent, int[])} constructor.
+ * Tests the rounding performed by the {@link GridExtent#GridExtent(AbstractEnvelope, GridRoundingMode, int[], GridExtent, int[])} constructor.
*/
@Test
public void testRoundings() {
@@ -71,7 +71,7 @@ public final strictfp class GridExtentTest extends TestCase {
env.setRange(2, 1.49998, 3.50001); // Round to [1…4), stored as [1…2] (not [1…3]) because the span is close to 2.
env.setRange(3, 1.49999, 3.50002); // Round to [1…4), stored as [2…3] because the upper part is closer to integer.
env.setRange(4, 1.2, 3.8); // Round to [1…4), stores as [1…3] because the span is not close enough to integer.
- GridExtent extent = new GridExtent(env, null, null);
+ GridExtent extent = new GridExtent(env, GridRoundingMode.NEAREST, null, null, null);
assertExtentEquals(extent, 0, 1, 2);
assertExtentEquals(extent, 1, 1, 2);
assertExtentEquals(extent, 2, 1, 2);
diff --git a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
index 47148f5..c6ebd8f 100644
--- a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
+++ b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
@@ -206,7 +206,7 @@ public final strictfp class GridGeometryTest extends TestCase {
0, 0.5, -90,
0.5, 0, -180,
0, 0, 1));
- final GridGeometry grid = new GridGeometry(PixelInCell.CELL_CORNER, gridToCRS, envelope);
+ final GridGeometry grid = new GridGeometry(PixelInCell.CELL_CORNER, gridToCRS, envelope, GridRoundingMode.NEAREST);
assertExtentEquals(
new long[] {370, 40},
new long[] {389, 339}, grid.getExtent());
@@ -237,7 +237,7 @@ public final strictfp class GridGeometryTest extends TestCase {
0.5, 0, 0, -180,
0, 0, 2, 3,
0, 0, 0, 1));
- final GridGeometry grid = new GridGeometry(PixelInCell.CELL_CORNER, gridToCRS, envelope);
+ final GridGeometry grid = new GridGeometry(PixelInCell.CELL_CORNER, gridToCRS, envelope, GridRoundingMode.NEAREST);
assertExtentEquals(
new long[] {336, 20, 4},
new long[] {401, 419, 10}, grid.getExtent());
diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java b/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java
index c707a04..0efa2b7 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java
@@ -22,6 +22,7 @@ import org.apache.sis.measure.NumberRange;
import org.apache.sis.util.Numbers;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.util.Numerics;
/**
@@ -29,7 +30,7 @@ import org.apache.sis.util.resources.Errors;
* Values may be {@code long} or {@code double} types.
*
* @author Martin Desruisseaux (MPO, Geomatys)
- * @version 0.8
+ * @version 1.0
* @since 0.8
* @module
*/
@@ -203,8 +204,8 @@ abstract class SequenceVector extends Vector implements Serializable {
}
/** Returns the increment between all consecutive values */
- @Override public Number increment(final double tolerance) {
- return Numbers.wrap(increment, type);
+ @Override public final Number increment(final double tolerance) {
+ return Numerics.valueOf(increment); // Always Double even if data type is Float.
}
/** Computes the minimal and maximal values in this vector. */
@@ -252,11 +253,6 @@ abstract class SequenceVector extends Vector implements Serializable {
@Override public String stringValue(final int index) {
return String.valueOf(floatValue(index));
}
-
- /** Returns the increment between all consecutive values */
- @Override public Number increment(final double tolerance) {
- return (float) increment;
- }
}
diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java b/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
index 90b1fe4..2a6ff93 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
@@ -714,7 +714,11 @@ search: for (;;) {
double inc = (doubleValue(--i) - first) / i; // First estimation of increment.
final int pz = Math.max(0, Math.min(i, (int) Math.rint(-first / inc))); // Presumed index of value zero.
if (doubleValue(pz) == 0) {
- inc = (pz == i) ? -doubleValue(pz-1) : doubleValue(pz+1); // Presumed less subject to rounding errors.
+ final Number value = (pz == i) ? get(pz-1) : get(pz+1); // Value adjacent to zero.
+ if (!(value instanceof Float)) {
+ inc = value.doubleValue(); // Presumed less subject to rounding errors.
+ if (pz == i) inc = -inc;
+ }
}
if (type == Numbers.FLOAT) {
while (i >= 1) {