0

Getting the following sets of numbers -->

[5.1429,5.1429,5.1429,5.1429,5.1429,5.1429,5.1426]
[0.8333,0.8333,0.8333,0.8333,0.8333,0.8335]

When added, these give a whole number.

Now we have to display these numbers after rounding to 2 decimal places which look like this -->

[5.14, 5.14, 5.14, 5.14, 5.14, 5.14, 5.14]
[0.83, 0.83, 0.83, 0.833, 0.83, 0.83]

Which is not summing up to a whole number. This is causing the whole total to come something like 99.96 or 101.01.

Is there a way we can round the numbers so that the total comes to a whole number.

I am not looking for a way to round off the sum. i am looking for a way to manipulate the already rounded numbers (like 5.14,5.14.. etc) so that they give a whole number. (if there is a way.. that is :) )

user3783183
  • 23
  • 1
  • 6
  • Please post your code. what you have tried. so that other can help you. – Yagnesh Agola Jun 27 '14 at 13:04
  • possible duplicate of [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – Oleg Estekhin Jun 27 '14 at 13:07
  • @OlegEstekhin i really don't think so. He/she has a problem of rounded numbers not adding to a hundred due to rounding error – ford prefect Jun 27 '14 at 13:08
  • 1
    How would you round, say, `[1/3, 1/3, 1/3]` to one decimal place (for the sake of the argument) so the sum comes up as `1`? – tobias_k Jun 27 '14 at 13:09
  • @inquisitiveIdiot then re-read the second answer to the linked question, and also read [What Every Computer Scientist Should Know About Floating-Point Arithmetic](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). This question is essentially the same as linked, even if the wording or values differ. – Oleg Estekhin Jun 27 '14 at 13:09
  • @tobias_k 0.33, 0.33, 0.34 but as I said in my answer this is not really rounding. – peter.petrov Jun 27 '14 at 13:09
  • @peter.petrov So, the third item is more frequent than the other ones? ;-) – tobias_k Jun 27 '14 at 13:10
  • 1
    You only need to round the answer, so just round thee answer and it will be right. – Peter Lawrey Jun 27 '14 at 13:10
  • @tobias_k correct, that's a good point... – peter.petrov Jun 27 '14 at 13:11
  • I think you are asking the wrong question. What you really want to know is (I guess): How can I _display_ those numbers rounded to two decimal places without affecting the result of the calculation? – tobias_k Jun 27 '14 at 13:12

6 Answers6

2

You would introduce the smallest absolute rounding error - while keeping the total sum intact - if you sort the inputs by decreasing third decimal and round everything down, except for just enough numbers to reach the target.

As a simple example:

input

0.132, 0.226, 0.257, 0.385 // sums up to 1.00

sort by 3rd decimal (descending)

0.257, 0.226, 0.385, 0.132

round everything down

0.25, 0.22, 0.38, 0.13 // sums up to 0.98

round up just enough to reach a whole number

0.26, 0.23, 0.38, 0.13 // sums up to 1.00

In code (untested):

public void printRounded(double[] ds) {
    // create wrapper objects
    int n = ds.length;
    Wrapper[] ws = new Wrapper[n];
    for (int i = 0; i < n; i++)
        ws[i] = new Wrapper(i, (int)(ds[i] * 1000) % 10, (int)(ds[i] * 100));

    // sort by third decimal, descending
    Arrays.sort(ws, new Comparator<Wrapper>() {
        public int compare(Wrapper o1, Wrapper o2) {
            return o2.thirdDecimal.compareTo(o1.thirdDecimal);
        }
    });

    // find number of elements that must be rounded up and increment
    int sum = 0;
    for (int i = 0; i < n; i++)
        sum += ws[i].prefix;
    int numberToIncrement = 100 - (sum % 100);
    for (int i = 0; i < numberToIncrement ; i++)
        ws[i].prefix++;

    // sort back to input order
    Arrays.sort(ws, new Comparator<Wrapper>() {
        public int compare(Wrapper o1, Wrapper o2) {
            return o1.index.compareTo(o2.index);
        }
    });

    // print values
    for (int i = 0; i < n; i++) {
        System.out.println(ws[i].prefix / 100 + "." ws[i].prefix % 100);
    }
}

private class Wrapper {
    public Wrapper(int index, int thirdDecimal, int prefix) {
        this.index = index;
        this.thirdDecimal = thirdDecimal;
        this.prefix = prefix;
    }

    public int index;
    public int thirdDecimal;
    public int prefix;
}

Instead of using the custom formatting you could of course convert the ints back to double and use standard formatting.

Vincent van der Weele
  • 12,927
  • 1
  • 33
  • 61
  • In my answer, I was referring exactly to something like this. I didn't formalize it as Heuster did. Probably this algorithm is indeed optimal (in the context of what I was thinking about). So +1 from me. – peter.petrov Jun 27 '14 at 14:38
  • @peter.petrov I didn't see your answer till now; might be that I started typing before you answered. It's pretty much the same idea indeed :-) – Vincent van der Weele Jun 27 '14 at 15:32
1

There's no way to do this unless you abandon the firm requirement
for rounding your numbers (to 2 decimal places) and allow for

(a) truncating your numbers (after the 2nd decimal place);
(b) rounding up/down when actually you need to round down/up.

If you allow for these operations (on some of the numbers) instead of
rounding all of them by the book, then most probably you can come up
with some algorithm which would give you the same (integral) sum even
after the rounding/truncation operations is applied on the original
numbers. But again, this is not really rounding (as we know it from math).

peter.petrov
  • 38,363
  • 16
  • 94
  • 159
  • Here's an idea for that algorithm (although I don't think it's a good idea): 1) Round all the numbers down, memorizing the rounding error for each, 2) check the difference of the sum to the next whole number as `x*.01`, 3) add `0.01` to the `x` numbers with highest rounding error. – tobias_k Jun 27 '14 at 13:23
1

If I round the result, I get a whole number.

public static void main(String... ignored) {
    sum(5.1429, 5.1429, 5.1429, 5.1429, 5.1429, 5.1429, 5.1426);
    sum(0.8333, 0.8333, 0.8333, 0.8333, 0.8333, 0.8335);
}

private static void sum(double... xs) {
    double sum = 0;
    for (double x : xs) {
        sum += x;
    }
    System.out.printf("sum was %.2f%n", sum);
}

BTW: If you know you expect a whole number, you should round to a whole number, not two decimal places.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
0

One way you could do it would be to round to two decimal places then take each number an multiply it by a hundred and divide it by the sum of the numbers. This should get you closer to 100 and if you iterate over that enough times your result should approach 100. So basically (pseudcode)

 lsum = 0
 vecNums = //previous numbers
 while abs(nsum -100) >.01
    for i in vecNums
       i = (i*100)/sum(vecNums)
 return vecNums
ford prefect
  • 7,096
  • 11
  • 56
  • 83
0

You could just sum them up as you did, and then round the final sum as well.

MikeM
  • 342
  • 2
  • 10
-1

You can take a look at the Math.round(double a) method

System.out.println(Math.round(12.51));//Yields 13
System.out.println(Math.round(12.49));//Yields 12
System.out.println(Math.round(12.50));//Yields 13

from Round off in JAVA

Community
  • 1
  • 1