This is an automated email from the ASF dual-hosted git repository. aherbert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-rng.git
commit 085ee67661a1e98ff3cb114ab7c4ab47e0c6a05a Author: Alex Herbert <aherb...@apache.org> AuthorDate: Mon Jul 5 17:52:22 2021 +0100 Faster method for a signed double in [-1, 1) --- .../sampling/shape/UnitBallSamplerBenchmark.java | 27 +++++++++++++++++++++- .../rng/sampling/shape/UnitBallSampler.java | 18 ++++++++++----- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/sampling/shape/UnitBallSamplerBenchmark.java b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/sampling/shape/UnitBallSamplerBenchmark.java index 87b911b..142d75d 100644 --- a/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/sampling/shape/UnitBallSamplerBenchmark.java +++ b/commons-rng-examples/examples-jmh/src/main/java/org/apache/commons/rng/examples/jmh/sampling/shape/UnitBallSamplerBenchmark.java @@ -151,13 +151,15 @@ public class UnitBallSamplerBenchmark { public static class Sampler1D extends SamplerData { /** Name for the signed double method. */ private static final String SIGNED_DOUBLE = "signedDouble"; + /** Name for the signed double method, version 2. */ + private static final String SIGNED_DOUBLE2 = "signedDouble2"; /** Name for the two doubles method. */ private static final String TWO_DOUBLES = "twoDoubles"; /** Name for the boolean double method. */ private static final String BOOLEAN_DOUBLE = "booleanDouble"; /** The sampler type. */ - @Param({BASELINE, SIGNED_DOUBLE, TWO_DOUBLES, BOOLEAN_DOUBLE}) + @Param({BASELINE, SIGNED_DOUBLE, SIGNED_DOUBLE2, TWO_DOUBLES, BOOLEAN_DOUBLE}) private String type; /** {@inheritDoc} */ @@ -178,6 +180,14 @@ public class UnitBallSamplerBenchmark { return new double[] {makeSignedDouble(rng.nextLong())}; } }; + } else if (SIGNED_DOUBLE2.equals(type)) { + return new Sampler() { + @Override + public double[] sample() { + // Sample [-1, 1) uniformly + return new double[] {makeSignedDouble2(rng.nextLong())}; + } + }; } else if (TWO_DOUBLES.equals(type)) { return new Sampler() { @Override @@ -739,6 +749,21 @@ public class UnitBallSamplerBenchmark { * @return the double */ private static double makeSignedDouble(long bits) { + // As per o.a.c.rng.core.utils.NumberFactory.makeDouble(long) but using a signed + // shift of 10 in place of an unsigned shift of 11. + return (bits >> 10) * 0x1.0p-53d; + } + + /** + * Creates a signed double in the range {@code [-1, 1)}. The magnitude is sampled evenly + * from the 2<sup>54</sup> dyadic rationals in the range. + * + * <p>Note: This method will not return samples for both -0.0 and 0.0. + * + * @param bits the bits + * @return the double + */ + private static double makeSignedDouble2(long bits) { // Use the upper 54 bits on the assumption they are more random. // The sign bit generates a value of 0 or 1 for subtraction. // The next 53 bits generates a positive number in the range [0, 1). diff --git a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/shape/UnitBallSampler.java b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/shape/UnitBallSampler.java index 75b3ded..3096584 100644 --- a/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/shape/UnitBallSampler.java +++ b/commons-rng-sampling/src/main/java/org/apache/commons/rng/sampling/shape/UnitBallSampler.java @@ -42,8 +42,13 @@ public abstract class UnitBallSampler implements SharedStateObjectSampler<double private static final int TWO_D = 2; /** The dimension for 3D sampling. */ private static final int THREE_D = 3; - /** The mask to extract the lower 53-bits from a long. */ - private static final long LOWER_53_BITS = -1L >>> 11; + /** + * The multiplier to convert the least significant 53-bits of a {@code long} to a {@code double}. + * Taken from o.a.c.rng.core.utils.NumberFactory. + * + * <p>This is equivalent to 1.0 / (1L << 53). + */ + private static final double DOUBLE_MULTIPLIER = 0x1.0p-53d; /** * Sample uniformly from a 1D unit line. @@ -239,10 +244,11 @@ public abstract class UnitBallSampler implements SharedStateObjectSampler<double * @return the double */ private static double makeSignedDouble(long bits) { + // As per o.a.c.rng.core.utils.NumberFactory.makeDouble(long) but using a signed + // shift of 10 in place of an unsigned shift of 11. // Use the upper 54 bits on the assumption they are more random. - // The sign bit generates a value of 0 or 1 for subtraction. - // The next 53 bits generates a positive number in the range [0, 1). - // [0, 1) - (0 or 1) => [-1, 1) - return (((bits >>> 10) & LOWER_53_BITS) * 0x1.0p-53d) - (bits >>> 63); + // The sign bit is maintained by the signed shift. + // The next 53 bits generates a magnitude in the range [0, 2^53) or [-2^53, 0). + return (bits >> 10) * DOUBLE_MULTIPLIER; } }