Trying to calculate (a+b)^n where n is a real value in a BigDecimal variable, but BigDecimal.pow is designed for accept only integer values.
If the input is within the magnitude range supported by double, and you do not need more than 15 significant digits in the result, convert (a+b) and n to double, use Math.pow, and convert the result back to BigDecimal.
As long as you are just using an integer for the exponent, you can use a simple loop to calculate x^y:
public static BigDecimal pow(BigDecimal x, BigInteger y) {
if(y.compareTo(BigInteger.ZERO)==0) return BigDecimal.valueOf(1); //If the exponent is 0 then return 1 because x^0=1
BigDecimal out = x;
BigInteger i = BigInteger.valueOf(1);
while(i.compareTo(y.abs())<0) { //for(BigDecimal i = 1; i<y.abs(); i++) but in BigDecimal form
out = out.multiply(x);
i = i.add(BigInteger.ONE);
}
if(y.compareTo(BigInteger.ZERO)>0) {
return out;
} else if(y.compareTo(BigInteger.ZERO))<0) {
return BigDecimal.ONE.divide(out, MathContext.DECIMAL128); //Just so that it doesn't throw an error of non-terminating decimal expansion (ie. 1.333333333...)
} else return null; //In case something goes wrong
}
or for a BigDecimal x and y:
public static BigDecimal powD(BigDecimal x, BigDecimal y) {
return pow(x, y.toBigInteger());
}
Hope this helps!
Related
I'm doing some really precise decimal calculations that I turn into reduced fractions at the end. The decimals need precision to 96 decimals.
Since the precision is so important I'm using BigDecimal and BigInteger.
The calculation of the BigDecimal always returns the correct decimal value, but my function for turning this decimal into a fraction fails for some cases
Let's say I have a BigDecimal d
d.toString() = 32.222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223
When my function is trying to turn this into a fraction it outputs
Decimal from BigDecimal is:
32.222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223
// Run the BigDecimal into getFraction
Denominator before reducing:
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Numerator before reducing:
32222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223
// Reduced fraction turns into:
-1/0
// But should output
290/9
Here's my function for reducing decimal into fraction:
static int[] getFraction(BigDecimal x) {
BigDecimal x1 = x.stripTrailingZeros();
//System.out.println(x.toString() + " stripped from zeroes");
//System.out.println(x1.scale());
// If scale is 0 or under we got a whole number fraction something/1
if(x1.scale() <= 0) {
//System.out.println("Whole number");
int[] rf = { x.intValue(), 1 };
return rf;
}
// If the decimal is
if(x.compareTo(BigDecimal.ZERO) < 0) {
// Add "-" to fraction when printing from main function
// Flip boolean to indicate negative decimal number
negative = true;
// Flip the BigDecimal
x = x.negate();
// Perform same function on flipped
return getFraction(x);
}
// Split BigDecimal into the intval and fractional val as strings
String[] parts = x.toString().split("\\.");
// Get starting numerator and denominator
BigDecimal denominator = BigDecimal.TEN.pow(parts[1].length());
System.out.println("Denominator :" + denominator.toString());
BigDecimal numerator = (new BigDecimal(parts[0]).multiply(denominator)).add(new BigDecimal(parts[1]));
System.out.println("Numerator :" + numerator.toString());
// Now we reduce
return reduceFraction(numerator.intValue(), denominator.intValue());
}
static int[] reduceFraction(int numerator, int denominator) {
// First find gcd
int gcd = BigInteger.valueOf(numerator).gcd(BigInteger.valueOf(denominator)).intValue();
//System.out.println(gcd);
// Then divide numerator and denominator by gcd
int[] reduced = { numerator / gcd, denominator / gcd };
// Return the fraction
return reduced;
}
If anyone would clarify if I have made any mistakes, I would greatly appreciate it!
** UPDATE **
Changed reduceFraction function:
Now returns a String[] instead of int[]
static String[] reduceFraction(BigDecimal numerator, BigDecimal denominator) {
// First find gcd
BigInteger nu = new BigInteger(numerator.toString());
BigInteger de = new BigInteger(denominator.toString());
BigInteger gcd = nu.gcd(de);
// Then divide numerator and denominator by gcd
nu = nu.divide(gcd);
de = de.divide(gcd);
String[] reduced = { nu.toString(), de.toString() };
// Return the fraction
return reduced;
}
getFraction returns:
// Now we reduce, send BigDecimals for numerator and denominator
return reduceFraction(num, den);
instead of
// Now we reduce
return reduceFraction(numerator.intValue(), denominator.intValue());
Still gets wrong answer from function
Output fraction now is
// Gcd value
gcd = 1
// Fraction is then:
32222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223/1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
//gcd Value should be:
gcd = 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
// Whit this gcd the fraction reduces to:
290/9
// Now we reduce
return reduceFraction(numerator.intValue(), denominator.intValue());
Well, this must fail in this case, because neither the numerator or denominator can fit into an int here.
The numerator becomes -1908874353, and the denominator becomes 0 after you call intValue() on them. You must carry with BigIntegers until the end of the computation.
Before converting them to int or long, if you must do so, you can check whether they can be converted to those types without loss of precision by checking them against Integer.MIN_VALUE, Integer.MAX_VALUE, Long.MIN_VALUE, and Long.MAX_VALUE.
You seem to be making this much harder than it needs to be. Here is my initial attempt:
public static BigInteger[] toRational(BigDecimal decimal)
{
int scale = decimal.scale();
if (scale <= 0) {
return new BigInteger[]{decimal.toBigInteger(), BigInteger.ONE};
} else {
BigInteger denominator = BigInteger.TEN.pow(scale);
BigInteger numerator = decimal.unscaledValue();
BigInteger d = numerator.gcd(denominator);
return new BigInteger[]{numerator.divide(d), denominator.divide(d)};
}
}
The rational number is always returned in lowest terms. Note that if decimal is 0 then 0/1 is returned as the rational. If decimal is negative then the rational is returned with the numerator negative.
I am translating .NET code to Java and ran into precision not matching issue.
.NET code:
private decimal roundToPrecision(decimal number, decimal roundPrecision)
{
if (roundPrecision == 0)
return number;
decimal numberDecimalMultiplier = Math.Round(number / roundPrecision, MidpointRounding.AwayFromZero);
return numberDecimalMultiplier * roundPrecision;
}
Calling roundToPrecision(8.7250, 0.05); function in the code above gives me 8.75 which is expected.
The conversion/translation of the function to Java is as follows. I din't find exact
Math.Round option.
Java code:
public double roundToPrecision(double number, double roundPrecision) {
if (roundPrecision == 0)
return number;
int len = Double.toString(roundPrecision).split("\\.")[1].length();
double divisor = 0d;
switch (len) {
case 1:
divisor = 10d;
break;
case 2:
divisor = 100d;
break;
case 3:
divisor = 1000d;
break;
case 4:
divisor = 10000d;
break;
}
double numberDecimalMultiplier = Math.round(number / roundPrecision);
double res = numberDecimalMultiplier * roundPrecision;
return Math.round(res * divisor) / divisor;
}
Calling roundToPrecision(8.7250, 0.05); in the Java code gives me 8.7and this is not correct.
I even tried modifying code with BigDecimal as follows in Java using the reference here C# Double Rounding but have no luck.
public double roundToPrecision(double number, double roundPrecision) {
if (roundPrecision == 0)
return number;
int len = Double.toString(roundPrecision).split("\\.")[1].length();
double divisor = 0d;
switch (len) {
case 1:
divisor = 10d;
break;
case 2:
divisor = 100d;
break;
case 3:
divisor = 1000d;
break;
case 4:
divisor = 10000d;
break;
}
BigDecimal b = new BigDecimal(number / roundPrecision);
b = b.setScale(len,BigDecimal.ROUND_UP);
double numberDecimalMultiplier = Math.round(b.doubleValue());
double res = numberDecimalMultiplier * roundPrecision;
return Math.round(res * divisor) / divisor;
}
Please guide me for what I need to do to fix this.
Here are couple of scenarios to try out.
number = 10.05; precision = .1; expected = 10.1;
number = 10.12; precision = .01; expected = 10.12;
number = 8.7250; precision = 0.05; expected = 8.75;
number = 10.999; precision = 2; expected = 10;
number = 6.174999999999999; precision = 0.05; expected = 6.20;
Note: I have over 60 thousand numbers and precision can vary from 1 decimal to 4 decimal places. The output of .NET should match exactly to Java.
The problem comes from how doubles vs decimals are stored and represented in memory. See these links for more specifics: Doubles Decimals
Let's take a look at how they each work in your code. Using doubles, with arguments of 8.725 and 0.05. number / roundPrecision gives 174.499..., since doubles aren't able to exactly represent 174.5. With decimals number / roundPrecision gives 174.5, decimals are able to represent this exactly. So then when 174.499... gets rounded, it gets rounded down to 174 instead of 175.
Using BigDecimal is a step in the right direction. There is an issue with how it's being used in your code however. The problem comes when you're creating the BigDecimal value.
BigDecimal b = new BigDecimal(number / roundPrecision);
The BigDecimal is being created from a double, so the imprecision is already there. If you're able to create the BigDecimal arguments from a string that would be much better.
public static BigDecimal roundToPrecision(BigDecimal number, BigDecimal roundPrecision) {
if (roundPrecision.signum() == 0)
return number;
BigDecimal numberDecimalMultiplier = number.divide(roundPrecision, RoundingMode.HALF_DOWN).setScale(0, RoundingMode.HALF_UP);
return numberDecimalMultiplier.multiply(roundPrecision);
}
BigDecimal n = new BigDecimal("-8.7250");
BigDecimal p = new BigDecimal("0.05");
BigDecimal r = roundToPrecision(n, p);
If the function must take in and return doubles:
public static double roundToPrecision(double number, double roundPrecision)
{
BigDecimal numberBig = new BigDecimal(number).
setScale(10, BigDecimal.ROUND_HALF_UP);
BigDecimal roundPrecisionBig = BigDecimal.valueOf(roundPrecision);
if (roundPrecisionBig.signum() == 0)
return number;
BigDecimal numberDecimalMultiplier = numberBig.divide(roundPrecisionBig, RoundingMode.HALF_DOWN).setScale(0, RoundingMode.HALF_UP);
return numberDecimalMultiplier.multiply(roundPrecisionBig).doubleValue();
}
Keep in mind that doubles cannot exactly represent the same values which decimals can. So the function returning a double cannot have the exact output as the original C# function which returns decimals.
The real problem here is that the Math.round has two definitions. One returns a long, while the other returns an int! When you provide a double it runs the one for a long. To fix this simply cast your input to a float, to make it run the one to return the int.
double numberDecimalMultiplier = Math.round((float)(number / roundPrecision));
I have the below codes round the forward rate to 15 decimal place. When _ForwardRate is 13,555.0, the result return is wrong.
public double round(double Number, int Decimal_Place) {
if (Number==0) return 0;
double _plug = 0.000001;
if (Number < 0) {
_plug = -0.000001;
}
//Sometime a number is rounded down to 2.22499999999 by java.
//Actual precision is 2.245. Without this plug, a 2 dp rounding result
//in 2.22 when it should be 2.23
double _newNumber = Number;
if (Decimal_Place==2) {
_newNumber = _newNumber+_plug;
}
double _number_abs = Math.abs(_newNumber);
double _factor = Math.pow(10, Decimal_Place);
double _rd = Math.round(_number_abs * _factor);
double _r = _rd/_factor;
if (Number <= 0)
_r = _r * -1;
return _r;
}
Double _ForwardRate = getForward_rate();
BigDecimal _fwdrate_bd = BigDecimal.valueOf(_ForwardRate.doubleValue());
_ForwardRate = round(new Double(_fwdrate_bd.doubleValue()), 15);
Current result
9,223.372036854777
Expected result
13,555.000000000000000
Your problem is that Math.round(double a) returns long, and you're overflowing.
One easy way to do this, is to use BigDecimal:
public static double round(double number, int decimalPlaces) {
return BigDecimal.valueOf(number)
.setScale(decimalPlaces, RoundingMode.HALF_UP)
.doubleValue();
}
This allows you to control the rounding mode. Note that the rounding done by Math.round() is a HALF_CEILING which isn't supported by setScale().
You might want to consider doing all you math using BigDecimal, if you need that level of precision.
Consider:
double _number_abs = Math.abs(_newNumber);
At this point, _number_abs contains the value 13555.0
double _factor = Math.pow(10, Decimal_Place);
Now _factor contains 1.0E15
double _rd = Math.round(_number_abs * _factor);
According to the Javadoc
Math.round() Returns the closest long to the argument, with ties rounding to positive infinity.
Since _number_abs * _factor is 1.3555E19, which is larger than Long.MAX_VALUE, the result is Long.MAX_VALUE, i.e. the "closest" Long to the given value.
I am currently trying to solve this problem as described here:
http://uva.onlinejudge.org/external/1/113.pdf
The plan was to implement a recursive function to derive the solution. Some of the code here comes from Rosetta code for determining the nth root.
// Power of Cryptography 113
import java.util.Scanner;
import java.math.BigDecimal;
import java.math.RoundingMode;
// k can be 10^9
// n <= 200
// p <= 10^101
class crypto {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while(in.hasNext()) {
// Given two integers (n,p)
// Find k such k^n = p
int n = in.nextInt();
BigDecimal p = in.nextBigDecimal();
System.out.println(nthroot(n,p));
}
}
public static BigDecimal nthroot(int n, BigDecimal A) {
return nthroot(n, A, .001);
}
public static BigDecimal nthroot(int n, BigDecimal A, double p) {
if(A.compareTo(BigDecimal.ZERO) < 0) return new BigDecimal(-1);
// we handle only real positive numbers
else if(A.equals(BigDecimal.ZERO)) {
return BigDecimal.ZERO;
}
BigDecimal x_prev = A;
BigDecimal x = A.divide(new BigDecimal(n)); // starting "guessed" value...
BigDecimal y = x.subtract(x_prev);
while(y.abs().compareTo(new BigDecimal(p)) > 0) {
x_prev = x;
BigDecimal temp = new BigDecimal(n-1.0);
x = (x.multiply(temp).add(A).divide(x.pow(temp.intValue())).divide(new BigDecimal(n)));
}
return x;
}
}
And here is the resulting error code:
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1616)
at crypto.nthroot(crypto.java:38)
at crypto.nthroot(crypto.java:24)
at crypto.main(crypto.java:19)
Anybody here for a working code snippet? Here we go:
public final class RootCalculus {
private static final int SCALE = 10;
private static final int ROUNDING_MODE = BigDecimal.ROUND_HALF_DOWN;
public static BigDecimal nthRoot(final int n, final BigDecimal a) {
return nthRoot(n, a, BigDecimal.valueOf(.1).movePointLeft(SCALE));
}
private static BigDecimal nthRoot(final int n, final BigDecimal a, final BigDecimal p) {
if (a.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException("nth root can only be calculated for positive numbers");
}
if (a.equals(BigDecimal.ZERO)) {
return BigDecimal.ZERO;
}
BigDecimal xPrev = a;
BigDecimal x = a.divide(new BigDecimal(n), SCALE, ROUNDING_MODE); // starting "guessed" value...
while (x.subtract(xPrev).abs().compareTo(p) > 0) {
xPrev = x;
x = BigDecimal.valueOf(n - 1.0)
.multiply(x)
.add(a.divide(x.pow(n - 1), SCALE, ROUNDING_MODE))
.divide(new BigDecimal(n), SCALE, ROUNDING_MODE);
}
return x;
}
private RootCalculus() {
}
}
Just set SCALE to however precise you need the calculation to be.
That is expected if the resulting mathematical decimal number is non-terminating. The Javadocs for the 1-arg overload of divide state:
Throws:
ArithmeticException - if the exact quotient does not have a terminating decimal expansion
Use another overload of the divide method to specify a scale (a cutoff) (and a RoundingMode).
I found that a rounding error with my Java application. The method used to round was:
public static double round(double value,double precision)
{
return Math.round(value * precision) / precision;
}
This could have an error (i.e. round(138.515,100) should return 138.52, and returns 138.51)
So I've created the following rounder:
// Mikeldi's rounder
public static double round2DecimalPlaces(double value,int decimalPlaces)
{
int s = value<0?-1:1;
double p = 1;
for (int i = 0; i < decimalPlaces; i++) {
p*=10;
}
double n = (long) value;
double d = ((value*10*p)-(n*10*p));
d +=s*5;
d /= 10;
d = (long)d;
d /= p;
return d+n;
}
I created this method since other rounding methods added too much latency to the system (low latency system). This one is around 10 times faster than the previous.
Note: This rounder will only use to round to possitive decimalPlaces (or 0).
Is there any problems I haven't see with this new rounder?
Thanks,
The Math#round method is not broken. 138.515 can't be exactly represented as a double. To see the exact value, you can use:
System.out.println(new BigDecimal(138.515d));
which prints:
138.5149999999999863575794734060764312744140625
It is therefore accurate for round to return 138.51. If you need more precision than double can give, you can use BigDecimal.
EDIT
If BigDecimal is not an option, and if the number of decimals is smallish (say 3 or 4 because these are prices for example), you can use longs instead with the last 4 digits being the decimals. So 138.51d would be 1385100L instead.