I have a group of doubles with a varying number of decimal places. I have a need to get the SUM() of those values and then normalize them all so that they SUM() to 1. Furthermore I have a requirement that in the final results we limit the number of decimal places to 4. To accomplish this I have tried doing the following :
normalizationFactor = 1/sumOfAllDoublesInGroup;
for(Object myObject : myGroupOfObjects){
myObject.setDoubleValue = round(myObject.getDoubleValue * normalizationFactor),4);
}
private Double round (Double doubleValue, Integer decimalPlaces) {
if (places < 0) throw new IllegalArgumentException();
BigDecimal bd = new BigDecimal(value);
bd = bd.setScale(places, RoundingMode.HALF_UP);
return bd.doubleValue();
}
The drawback here is that after rounding I still cannot guarantee that the SUM() of all the doubles is still == 1. I would appreciate some help with that.
To be clear the only requirements are that
1) I get a set of numbers each having a varying number of decimal places.
2) When all is said and done each number is limited to 4 decimal places.
3) The final SUM() of all numbers in the group must EXACTLY = 1
There are many conversions in your code between double and BigDecimal. These conversions may be adding double precision error to your values. Please find the below code, all numbers are stored in BigDecimal.
List<BigDecimal> myGroupOfObjects = new ArrayList<>();
myGroupOfObjects.add(new BigDecimal("0.3"));
myGroupOfObjects.add(new BigDecimal("0.1"));
myGroupOfObjects.add(new BigDecimal("0.2"));
myGroupOfObjects.add(new BigDecimal("0.2"));
BigDecimal sumOfAllDoublesInGroup = new BigDecimal("0.8"); // #HardCoding
BigDecimal normalizationFactor = new BigDecimal("1.0")
.divide(sumOfAllDoublesInGroup);
BigDecimal result = new BigDecimal(0);
for(BigDecimal myObject : myGroupOfObjects){
myObject = myObject.multiply(normalizationFactor)
.setScale(4, RoundingMode.HALF_UP);
result = result.add(myObject);
}
Result:
0.3750
0.1250
0.2500
0.2500
------
1.0
Related
I've been trying to sum up decimal values using double in java and it doesn't work well, got a wrong answer.
I've already tried with Double, Float, BigDecimal.
{
double a = 2595.00;
double b = -1760.76;
double c = -834.00;
double d = -.24;
System.out.print(a+b+c+d);
}
The expected result should be "0" But Got 9.1038288019262836314737796783447265625E-15
You can use BigDecimal for this purpose and make sure you input the numbers as String in the BigDecimal constructor:
BigDecimal a = new BigDecimal("2595.00");
BigDecimal b = new BigDecimal("-1760.76");
BigDecimal c = new BigDecimal("-834.00");
BigDecimal d = new BigDecimal("-.24");
System.out.println(a.add(b).add(c).add(d));
Live Example
Output is:
0.00
From the Java docs for BigDecimal(String):
This is generally the preferred way to convert a float or double into
a BigDecimal, as it doesn't suffer from the unpredictability of the
BigDecimal(double) constructor.
Check this SO thread for why double results in a loss of precision.
As already pointed by the previous answers about double precision, the value here is very close to zero. You can see it with System.out.format as well.
System.out.format("%.14f%n",a+b+c+d);
System.out.format("%1.1f%n",a+b+c+d); //to print 0.0
I am making a phi (golden ratio) calculator, and I'm having an issue with the precision of the answers I get.
I realized that there seems to be a fixed number of digits that my answers can be up to, and afterwards it just truncates. At first I thought it was an issue with doubles, so I changed to BigDecimals. Yet the problem still persists.
Here was my original double logic:
public static final double PHI = 1.6180339887498948482045;
b = Double.parseDouble(field.getText());
a = b * PHI;
aPlusB = a + b;
System.out.println(a.toString());
Here's the code for my BigDecimal Logic:
public static final double PHI = 1.6180339887498948482045;
BigDecimal phi = new BigDecimal(calculationHolder.PHI);
MathContext context = new MathContext(15, RoundingMode.HALF_UP);
BigDecimal a = new BigDecimal(BigInteger.ZERO);
BigDecimal b = new BigDecimal(BigInteger.ZERO);
BigDecimal aPlusB = new BigDecimal(BigInteger.ZERO);
b = new BigDecimal(field.getText());
a = b.multiply(phi, context);
aPlusB = a.add(b, context);
System.out.println(a.toString());
Now if I were to make b = 1:
my double logic would return 1.618033988749895(many digits short of the real value it should be).
If I were to use my BigDecimal logic, it would return 1.61803398874989 (even less precise)
If I were to use a really large number like 123456789123456 for b,
My double logic would return 199757280943680.16, and by BigDecimal logic returns 199757280943680 (even less precise, and not even any decimals).
I'm confused about this behavior. It seems like, if anything, the BigDecimal logic is giving me even less precise answers, and I don't know why.
Could anyone please shed some light on this?
You specified 15 decimal digits of precision here:
new MathContext(15, RoundingMode.HALF_UP);
You got 15 decimal digits of precision here:
1.61803398874989
You got exactly what you asked for. Do you understand what the first constructor parameter does?
I want to substract 2 double values, and I have tried the following code.
double val1 = 2.0;
double val2 = 1.10;
System.out.println(val1 - val2);
and I got the output as,
0.8999999999999999
For getting output as 0.9 I tried with BigDecimal as follows,
BigDecimal val1BD = new BigDecimal(val1);
BigDecimal val2BD = new BigDecimal(val2);
System.out.println(val1BD.subtract(val2BD));
And I got the output as,
0.899999999999999911182158029987476766109466552734375
Then I tried with BigDecimal.valueOf()
val1BD = BigDecimal.valueOf(val1);
val2BD = BigDecimal.valueOf(val2);
System.out.println(val1BD.subtract(val2BD));
And finally I got the output as 0.9.
My question is what is the difference between case 2 & case 3?
In case 2 why I got the output like that?
BigDecimal.valueOf(double d) uses canonical String representation of double value, internally Double.toString(double) is used, that's why you are getting 0.9 in second case.
Note: This is generally the preferred way to convert a double (or
float) into a BigDecimal, as the value returned is equal to that
resulting from constructing a BigDecimal from the result of using
Double.toString(double).
While with new BigDecimal(0.9) it converts value to exact floating point representation of double value without using String representation,
Translates a double into a BigDecimal which is the exact decimal
representation of the double's binary floating-point value.
...
NOTES :
The results of this constructor can be somewhat unpredictable.
...
FOR EXAMPLE :
BigDecimal bd1 = new BigDecimal(Double.toString(0.9));
BigDecimal bd2 = new BigDecimal(0.9);
System.out.println(bd1);
System.out.println(bd2);
OUTPUT :
0.9
0.90000000000000002220446049250313080847263336181640625
Just for those others that got here looking for some other issue with BigDecimal(not related to the question above)...
remember to give a mathContext to the methods to avoid certain problems e.g.
MathContext mc = new MathContext(10, RoundingMode.HALF_UP);
BigDecimal hitRate = new BigDecimal(totalGetValuesHitted).divide(new BigDecimal(totalGetValuesRequested), mc);
BigDecimal missRate = new BigDecimal(1.0, mc).subtract(hitRate, mc);
I have done a reading about number conversions in Java because I need to format a number inside my Android application.
I'm currently holding a long variable with 4 digits
long variable_1 = 2203;
And this is what I'm looking to print
220.3
What I have done so far
variable_1 = 2203;
BigDecimal bd = new BigDecimal(variable_1);
bd = bd.setScale(1, BigDecimal.ROUND_HALF_UP);
txtView_variable_1.setText(String.valueOf(bd));
Result
2203.0
What am I doing wrong here?
If you change the scale of a BigDecimal, it will return a new BigDecimal with the specified scale but with the same numerical value. It won't change the interpretation of the unscaled number. The underlying scaled number will be rescaled.
You should give the scale at the initialization of the BigDecimal in order for it to interpret correctly your unscaled number :
long variable_1 = 2203;
BigDecimal bd = new BigDecimal(BigInteger.valueOf(variable_1), 1);
System.out.println(String.valueOf(bd));
Which outputs :
220.3
I had the same problem to round at 0.1
I managed it like this:
private BigDecimal roundBigDecimal(BigDecimal valueToRound) {
int valeurEntiere = valueToRound.intValue();
String str = String.valueOf(valeurEntiere);
int rounding = valeurEntiere == 0 ? 1 : str.length() + 1;
return new BigDecimal(String.valueOf(valueToRound), new MathContext(rounding, RoundingMode.HALF_UP));
}
In fact the int rounding depends of the number of digit of the number.
I have below code:
Double a = new Double((123456798/1000000)); //123456798 this value comes from client side as a `int`
DecimalFormat df = new DecimalFormat("###.###");
log.info("a :"+a+" df "+df.format(a.doubleValue()));
output:
a :123.0 df 123
//i want output like this, a :123.xxx fd 123.xxx
please help
UPDATE:
123456798 this value comes from client side as a int so i cant do it as 123456798.0 (or something)
123456798 and 1000000 are int literals, so dividing them will use integer arithmetic, and yield 123.
Instead, you could use floating point literals in order to use floating point arithmetic:
Double a = new Double((123456798.0/1000000.0));
DecimalFormat df = new DecimalFormat("###.###");
log.info("a :"+a+" df "+df.format(a.doubleValue()));
Any one value in the division should be float or double.
Double a = new Double((123456798.0/1000000));
or
Double a = new Double((123456798/1000000.0));
if you are getting these values in variables, then multiply it with 1.0
like
Double a = new Double((variable*1.0/1000000));
Put it like that
Double a = new Double((123456798.0/1000000.0)); // <- note ".0"
the reason of the misbehavior is the integer division:
123456798/1000000
is the integer value, while
123456798.0/1000000.0
is the floating point one (double)
Double a = new Double((123456798/1000000));
You are doing integer division here. Make one of the constants a double, so that floating-point division is done. Also, why are you using Double? It's better to use the primitive type double.
double a = 123456798.0 / 1000000;
Or simply, since they are constants:
double a = 123.456789;
You perform an integer division, thats why a is incorrect:
Double a = new Double(123456798.0/1000000);