I need to generate random BigDecimal value from given range. How to do it in Java?
class BigDecRand {
public static void main(String[] args) {
String range = args[0];
BigDecimal max = new BigDecimal(range + ".0");
BigDecimal randFromDouble = new BigDecimal(Math.random());
BigDecimal actualRandomDec = randFromDouble.divide(max,BigDecimal.ROUND_DOWN);
BigInteger actualRandom = actualRandomDec.toBigInteger();
}
}
I do this that way
public static BigDecimal generateRandomBigDecimalFromRange(BigDecimal min, BigDecimal max) {
BigDecimal randomBigDecimal = min.add(new BigDecimal(Math.random()).multiply(max.subtract(min)));
return randomBigDecimal.setScale(2,BigDecimal.ROUND_HALF_UP);
}
And the way I run it:
BigDecimal random = Application.generateRandomBigDecimalFromRange(
new BigDecimal(-1.21).setScale(2, BigDecimal.ROUND_HALF_UP),
new BigDecimal(21.28).setScale(2, BigDecimal.ROUND_HALF_UP)
);
Previous answers don't address the loss of precision that results from scaling values with arbitrarily large numbers of digits by double precision floating point values with a relatively small number of digits. The following implementation of BigRandom can generate random BigInteger and BigDecimal values at specified precision:
// The short version
public static BigDecimal between(BigDecimal min, BigDecimal MAX) {
int digitCount = Math.max(min.precision(), MAX.precision());
int bitCount = (int)(digitCount / Math.log10(2.0));
// convert Random BigInteger to a BigDecimal between 0 and 1
BigDecimal alpha = new BigDecimal(
new BigInteger( bitCount, new Random() )
).movePointLeft(digitCount);
return min.add(MAX.subtract(min).multiply(alpha, new MathContext(digitCount)));
}
// Full Implementation
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.util.Random;
public class BigRandom {
private static Random defaultRandom = new Random();
// Constants:
private static double log2 = Math.log10(2.0);
// Computes number of bits needed to represent an n digit positive integer.
private static int bitCount(int n) {
return (int)( n / log2 );
}
// Static Methods for generating Random BigInteger values:
public static BigInteger nextBigInteger(int precision) {
return nextBigInteger(precision, defaultRandom);
}
public static BigInteger nextBigInteger(int precision, Random r) {
return new BigInteger(bitCount(precision), r);
}
public static BigInteger nextBigInteger(BigInteger norm) {
return nextBigInteger(norm, defaultRandom);
}
public static BigInteger nextBigInteger(BigInteger norm, Random r) {
BigDecimal bdNorm = new BigDecimal(norm);
int precision = bdNorm.precision() - bdNorm.scale();
return bdNorm.multiply(nextBigDecimal(precision, r), new MathContext(precision + 1)).toBigInteger();
}
public static BigInteger between(BigInteger min, BigInteger MAX) {
return between(min, MAX, defaultRandom);
}
public static BigInteger between(BigInteger min, BigInteger MAX, Random r) {
return min.add( nextBigInteger( MAX.subtract(min), r ) );
}
// Static Methods for generating Random BigDecimal values:
public static BigDecimal nextBigDecimal(int scale) {
return nextBigDecimal(scale, defaultRandom);
}
public static BigDecimal nextBigDecimal(int scale, Random r) {
BigInteger bi = nextBigInteger(scale, r); // generate random BigInteger with a number of digits equal to scale.
BigDecimal bd = new BigDecimal(bi); // convert BigInteger to a BigDecimal
return bd.movePointLeft(bd.precision()); // move the decimal point all the way to the left
}
public static BigDecimal nextBigDecimal(BigDecimal norm, int scale) {
return nextBigDecimal(norm, scale, defaultRandom);
}
public static BigDecimal nextBigDecimal(BigDecimal norm, int scale, Random r) {
return norm.multiply( nextBigDecimal( scale, r ), new MathContext( (norm.precision() - norm.scale()) + scale) );
}
public static BigDecimal between(BigDecimal min, BigDecimal MAX) {
return between(min, MAX, defaultRandom);
}
public static BigDecimal between(BigDecimal min, BigDecimal MAX, Random r) {
return min.add(
nextBigDecimal(
MAX.subtract(min),
Math.max( min.precision(), MAX.precision() ),
r
)
);
}
public static void main(String[] args) {
// Make a BigInteger independently from this implementation.
int bc = ((150 - defaultRandom.nextInt(50)) * 8) - defaultRandom.nextInt(8);
BigInteger bi = new BigInteger(bc, defaultRandom);
String bistr = bi.toString();
int precision = bistr.length();
System.out.println("Independently generated random BigInteger:\n" + bistr);
System.out.println("\tprecision: " + bistr.length());
System.out.println("\n\n------------------------\n\n");
// demonstrate nextBigInteger(precision)
System.out.println("demonstrate nextBigInteger(precision = " + precision + "):\n");
for (int i = 0; i < 5; i++) {
BigInteger bii = nextBigInteger(precision);
String biistr = bii.toString();
System.out.println("iteration " + i + " nextBigInteger(precision = " + precision + "):\n\t" + biistr);
System.out.println("\tprecision: " + biistr.length() + " == " + precision + " : " + ( biistr.length() == precision ));
}
System.out.println("\n\n------------------------\n\n");
// demonstrate nextBigInteger(norm)
System.out.println("demonstrate nextBigInteger(\n\tnorm = " + bi + "\n):\n");
for (int i = 0; i < 5; i++) {
BigInteger bii = nextBigInteger(bi);
String biistr = bii.toString();
System.out.println("iteration " + i + " nextBigInteger(norm = ... ):\n\t" + biistr);
System.out.println("\tprecision: " + biistr.length() + " <= " + precision + " : " + ( biistr.length() <= precision ));
System.out.println("\t( bii <= bi ) = " + (bii.compareTo(bi) <= 0));
}
BigInteger bin = bi.negate();
System.out.println("\n\n------------------------\n\n");
// demonstrate between(min, MAX)
System.out.println("demonstrate between(\n\tmin = " + bin + ",\n\tMAX = " + bi + "\n):\n");
for (int i = 0; i < 5; i++) {
BigInteger bii = between(bin, bi);
String biistr = bii.toString();
System.out.println("iteration " + i + " between(norm = ... ):\n\t" + biistr);
System.out.println("\tprecision: " + biistr.length() + " <= " + precision + " : " + ( biistr.length() <= precision ));
System.out.println("\t( bii >= -bi ) = " + (bii.compareTo(bin) >= 0));
System.out.println("\t( bii < bi ) = " + (bii.compareTo(bi) < 0));
}
System.out.println("\n\n------------------------\n\n");
// Make a BigDecimal independently from this implementation.
BigDecimal bd = new BigDecimal(Double.MAX_VALUE);
for (int i = 10; i < 50; i = i + 10) {
bd = bd.add( new BigDecimal(defaultRandom.nextDouble()).pow(i) );
}
System.out.println("Independently generated random BigDecimal:\n" + bd);
System.out.println("\tprecision: " + bd.precision() + " scale: " + bd.scale());
System.out.println("\n\n------------------------\n\n");
// demonstrate nextBigDecimal(scale)
System.out.println("demonstrate nextBigDecimal(scale = " + bd.scale() + "):\n");
for (int i = 0; i < 5; i++) {
BigDecimal bdi = nextBigDecimal(bd.scale());
System.out.println("iteration " + i + " nextBigDecimal(scale = " + bd.scale() + "):\n\t" + bdi);
System.out.println("\tprecision: " + bdi.precision() + " scale: " + bdi.scale());
}
System.out.println("\n\n------------------------\n\n");
// demonstrate nextBigDecimal(norm, scale)
System.out.println("demonstrate nextBigDecimal(\n\tnorm = " + bd + ",\n\tscale = " + bd.scale() + "\n):\n");
for (int i = 0; i < 5; i++) {
BigDecimal bdi = nextBigDecimal(bd, bd.scale());
System.out.println("iteration " + i + " nextBigDecimal(norm = ..., scale = " + bd.scale() + "):\n\t" + bdi);
System.out.println("\tprecision: " + bdi.precision() + " scale: " + bdi.scale());
System.out.println("\t( bdi <= bd ) = " + (bdi.compareTo(bd) <= 0));
}
System.out.println("\n\n------------------------\n\n");
// demonstrate between(min, MAX)
BigDecimal bdn = bd.negate();
System.out.println("demonstrate between(\n\tmin = " + bdn + ",\n\tMAX = " + bd + "\n):\n");
for (int i = 0; i < 5; i++) {
BigDecimal bdi = between(bdn, bd);
System.out.println("iteration " + i + " between(-bd, bd):\n\t" + bdi);
System.out.println("\tprecision: " + bdi.precision() + " scale: " + bdi.scale());
System.out.println("\t( bdi >= -bd ) = " + (bdi.compareTo(bdn) >= 0));
System.out.println("\t( bdi < bd ) = " + (bdi.compareTo(bd) < 0));
}
}
}
The following example attempts to clarify the reason why previous answers omit very large intervals of potentially valid random values.
Please consider an arbitrarily small epsilon value:
e = x 10^(-infinity)
and an interval:
[0 + e, 1 + e]
.
As we try to approximate this conceptual range with larger and larger numeric values substituted in place of infinity, the scale of the range's endpoints grows well beyond the scale of Math.random()
which returns double precision floating point numbers.
Conceptually:
BigDecimal e = ... // arbitrarily small value.
BigDecimal min = new BigDecimal(0.0).add(e);
BigDecimal MAX = new BigDecimal(1.0).add(e);
BigDecimal norm = MAX.subtract(min); // 1.0
BigDecimal randBigDecimal = min.add(
norm.multiply(
new BigDecimal(
Math.random()
)
)
); // equivalent to e + (1.0 * Math.random())
If Math.random()
returns 0, randBigDecimal equals: e;
If Math.random()
returns Double.MIN_VALUE, randBigDecimal equals: Double.MIN_VALUE + e
.
We can innumerate all possible values of randBigDecimal like so:
double d = 0.0;
// Don't actually run this loop! :)
while (d < 1.0) {
System.out.println(e + d);
d = Math.nextUp(d);
}
The more that the scale of e exceeds the scale of Java's double precision floating point numeric type, i.e. the primary motivation for using BigDecimal, the larger the gaps this algorithm leaves between e + d
and e + Math.nextUp(d)
.
In any case, this algorithm always leaves out min + 2e, min + 3e, ..., min + (N-1)e, min + Ne. For all integers [2, N] such that (new BigDecimal(N).times(new BigDecimal(Double.MIN_VALUE))).scale() > e.scale()
.
Of course, many infinities of numbers exist between e and 2e, but we might like our random BigDecimal algorithm to cover at least all values with the same scale as Math.max(min.scale(), MAX.scale())
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With