Floating-point numbers are mythological beasts of wandering behavior. Or at least they are so if you do not know how to tame them before using them.
Using floating points, I can add 3 numbers in an order and get a result different than adding those same numbers in another order:
a + b + c =?= c + a + b
I briefly discuss the subject in some answers:
-
in this one , I have to make a sum
f(n)*b
for several values of n
; one of the strategies I did not put the b
in evidence, then the sum was sum = f(1)*b + f(2)*b + f(3)*b
, with 3 multiplications and 2 sums, in the other alternative I only multiply at the end of the sum sum = b*(f(1) + f(2) + f(3))
with 2 sums and 1 multiplication; and, no, they are not equivalent sums when taking into account the precision of the floating point
-
in this other answer , I used this fact of loss of precision as a stop condition in an infinite sum, including I show that there are cases where you add
a + y == a, y != 0
, therefore (a + y) + y == a
, but that there can be a + (y + y) > a
So what would be the way to try to ensure as much accuracy as possible in the sum? Adding from the least significant value to the most significant! Basically change the order of for
proposed by @Paulo R. F. Amorim . It is also possible to do this in recursion proposed by @Marcos Andrade with a little more caution.
Solution with iteration
double sum = 0d;
int n = 4
for (int i = n; i >= 1; i--) {
sum += 1d/i;
}
I am using the notation indicating that the number is a double
as proposed by @Isac . Another alternative would be to 1.0/i
, which forces the number to be interpreted as double
of any sort.
Recursive solution 1
The idea here is to have an interface function that calls the recursive function itself. Let's pass in the recursive function the sum accumulated until then, so we can be adding the smallest elements first to then add the most significant element.
This strategy somewhat resembles some solutions used in prolog when you do not want anyone browsing the database to know that the list to be passed as a third non-intuitive argument must be an empty list.
public static double somaInverso(int n) {
return somaInversoPrivado(n, 0.0);
}
private static double somaInversoPrivado(int n, double acumulado) {
if (n == 1) {
return 1.0 + acumulado;
}
return somaInversoPrivado(n-1, 1.0/n + acumulado);
}
Note that here I left the somaInversoPrivado
method as a private method to the class that does these calculations. It is an auxiliary function whose API should not be exposed.
Recursive solution 2
Here things happen similarly to each other, but instead of sending the cumulative sum, I send in which step in the recursion I am. I have to stop when I'm at the n
step. The idea is that the result of step i
is 1.0/i + sum(i+1, n)
, this ensures that in the last recursive step the smallest value is returned, which will then be summed with the second smaller value and so on.
public static double somaInverso(int n) {
return somaInversoPrivado(1, n);
}
private static double somaInversoPrivado(int i, int n) {
if (n == i) {
return 1.0/i;
}
return 1.0/i + somaInversoPrivado(i + 1, n);
}