I'm trying to calculate the average of many (more than 200) long
(primitive type) values stored in an array. Usually you add all the numbers and divide it by the number of the values. But this is not possible in this case since there is no primitive datatype in Java capable of holding such a large number, isn't it? Will java.math.BigInteger
help here?
-
1Related: http://stackoverflow.com/q/1930454/1223693 – tckmn Jan 06 '14 at 14:38
-
As you're asking about performance.... 1. don't optimize if you don't have to. 2. Using a hand-crafted mutable 128-bit integer would be at least twice as fast, but more work and error-prone. – maaartinus Jan 06 '14 at 15:28
4 Answers
Yes it will help you. An BigInteger
can be as big as you want. Till there is not enough RAM.
With BigInteger bigInt = BigInteger.valueOf(long);
you can convert the Long
to an BigInteger
.
And an BigInteger
is immutable. So if you divide it like this bigInt = bigInt.divide(BigInteger.valueOf(200));
You have to reassign it.
A more precise option would be the method BigInteger.divideAndRemainder()
.
-
1Of course, as you know, this will not necessarily give the big integer closest to the average of the values in the array. Big integer division truncates so that if the true average were `10.99` this approach would return `10`. – High Performance Mark Jan 06 '14 at 15:03
-
2Guava's `com.google.common.math.BigIntegerMath` has a `divide` method accepting an `RoundingMode`. – maaartinus Jan 06 '14 at 15:30
Kai has a very good answer, but I'll just throw out there that if you know the exact number of values you are trying to average (and you do, since you said it was an array), then you can divide each value by N before adding them all up. Then you would never exceed the limit of long
.
Example: the limit of long
is roughly 9.22e18, so let's do an average near that limit:
long[] arr = {Math.round(5e18), Math.round(9e18)};
double avg = 0.0d;
int size = arr.length;
for (int i = 0; i < size; i++) {
long l = arr[i];
double tmp = l/(double)size;
avg += tmp;
}
(The Math.round()
calls are needed since exponential notation numbers are doubles in Java.)

- 7,186
- 1
- 26
- 44
-
But that won't be pecise since the decimal points caused by that division fall away, right? – felixd Jan 06 '14 at 14:50
-
However you would lose a lot of accuracy (potentially all of it if the values are less than the length of the array). This might work if you stored them in doubles. `Double`s would be faster than `BigInteger`. `BigInteger` will be most accurate. – Tim B Jan 06 '14 at 14:50
-
But then it would be possible that the data range of the `double` would not be enough. – felixd Jan 06 '14 at 14:51
-
1No, you divide as you go. d = i[0]/3.0; d+= i[1]/3.0; d+=i[2]/3.0; No risk of overflow. – Tim B Jan 06 '14 at 14:52
-
1Oh yes, well, I assumed the average of the array of longs would be a `double`. You'd have to store each `arr[i]/N` as a double... if the desired result is a long, you could recast or round at the end. – dcsohl Jan 06 '14 at 16:30
-
-
If you know that the sum of all the long values in your array
/list
will never exceed the Long.MAX_VALUE
value, you can use the following to calculate the average:
// For long type array
long[] array = ...;
double average = LongStream.of(array).average().getAsDouble();
// For long type list
List<Long> list = ...;
double average = list.stream().mapToLong(n -> n).average().getAsDouble();
But if you're unsure about the sum of your list, and that it can even exceed the maximum limit that long primitive provides, then use the following method to calculate the average:
// For long type array
long[] array = ...;
BigInteger sum = LongStream.of(array).mapToObj(BigInteger::valueOf).reduce((x, y) -> x.add(y)).get();
BigDecimal average = new BigDecimal(sum).divide(BigDecimal.valueOf(array.length));
// For long type list
List<Long> list = ...;
BigInteger sum = list.stream().map(BigInteger::valueOf).reduce((x, y) -> x.add(y)).get();
BigDecimal average = new BigDecimal(sum).divide(BigDecimal.valueOf(list.size()));
This technique works from Java 8 and following imports are required:
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.stream.LongStream;

- 2,973
- 1
- 18
- 22
-
1Thank you! This approach is very simple but the downside is that is requires Java 1.8 in order to run. – felixd Mar 30 '14 at 12:53
-
1@felixd: Agreed. But if one can update to Java 1.8, this approach provides a very neat solution. I just wanted you to be aware of this approach as well. :) – Aman Agnihotri Mar 30 '14 at 13:14
- An average of a
long
is unlikely to be along
, so you need to decide for a type to keep your result.BigDecimal
(not aBigInteger
though) could be a good choice. You will probably need to define the accuracy of your result as well. Use a smarter algorithm to compute averages on massive collections. I do not have a definitive choice for you, but you could try something like this:
- initialise the value for your result as
BigDecimal
zero; - compute a sum of next
M
elements and divide it byN
whereN
is the total number of elements andM<N
. The choice ofM
depends on the scale of your values and the accuracy your want to achive -- the larger the better, but think of your original problem; - add this value to the result and go back to step 2 until all elements are used up
- initialise the value for your result as

- 9,834
- 6
- 39
- 62
-
1Your algorithm isn't smart, it's just complicated. For simplicity and speed, compute the sum (in a `BigInteger`) and then divide. – maaartinus Jan 06 '14 at 15:26
-
If "massive" is properly massive then a massive amount of memory will be needed to hold a sum in a `BigInteger`: the algorith is for "massive" arrays as the author specified. `BigInteger` cannot hold an accurate average of any numbers in a general case – Oleg Sklyar Jan 06 '14 at 15:40
-
@Oleg I can't remember typing "massive" anywhere ... And what do you mean with "massive amount of memory"? – felixd Jan 06 '14 at 18:09
-
-
1@OlegS.: You're wrong again. For the sum of a billion longs you'd need 96 bits only. – maaartinus Jan 07 '14 at 14:07