views:

462

answers:

8

I need to evaluate the sum of the row: 1/1+1/2+1/3+...+1/n. Considering that in C++ evaluations are not complete accurate, the order of summation plays important role. 1/n+1/(n-1)+...+1/2+1/1 expression gives the more accurate result. So I need to find out the order of summation, which provides the maximum accuracy. I don't even know where to begin. Preferred language of realization is C++. Sorry for my English, if there are any mistakes.

+2  A: 

http://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic You can find libraries with ready for use implementation for C/C++.

For example http://www.apfloat.org/apfloat/

Ray
Actually I need to write "my own program". Because it's some kind of educational task.
Constantius
Yes, but instead of using low precision double's you could use apfloats and get as high precision as needed.
Ray
+1  A: 

I'm not sure about the order of summation playing an important role, I havent heard that before. I guess you want to do this in floating point arithmetic so the first thing is to think more inline of (1.0/1.0 + 1.0/2.0+1.0/3.0) - otherwise the compiler will do integer division

to determine order of evaluation, maybe a for loop or brackets?

e.g.

float f = 0.0;
for (int i=n; i>0; --i) 
{
    f += 1.0/static_cast<float>(i);
}

oh forgot to say, compilers will normally have switches to determine floating point evaluation mode. this is maybe related to what you say on order of summation - in visual C+ these are found in code-generation compile settings, in g++ there're options -float that handle this

actually, the other guy is right - you should do summation in order of smallest component first; so 1/n + 1/(n-1) .. 1/1

this is because the precision of a floating point number is linked to the scale, if you start at 1 you'll have 23 bits of precision relative to 1.0. if you start at a smaller number the precision is relative to the smaller number, so you'll get 23 bits of precision relative to 1xe-200 or whatever. then as the number gets bigger rounding error will occur, but the overall error will be less than the other direction

danclarke_2000
using floating-point math, add 1e-19 to 1 and look at the result.
Joey
Yeah, you want to sum up all your small terms so that the effect of adding 1 will be less likely to shift them out of the mantissa part of your float.
paxdiablo
Task definition tells that it plays role.In floating point, sure. I wrote the row "in mathematic way".And yes, I thought about just simple loop, but it's not looking very good, because of large number of such permutations.
Constantius
+5  A: 

The reason for the lack of accuracy is the precision of the float, double, and long double types. They only store so many "decimal" places. So adding a very small value to a large value has no effect, the small term is "lost" in the larger one.

The series you're summing has a "long tail", in the sense that the small terms should add up to a large contribution. But if you sum in descending order, then after a while each new small term will have no effect (even before that, most of its decimal places will be discarded). Once you get to that point you can add a billion more terms, and if you do them one at a time it still has no effect.

I think that summing in ascending order should give best accuracy for this kind of series, although it's possible there are some odd corner cases where errors due to rounding to powers of (1/2) might just so happen to give a closer answer for some addition orders than others. You probably can't really predict this, though.

Steve Jessop
The problem is that the series diverges, so even if you add the terms in ascending order you can get to a point where adding each new term one at a time has no effect.
Brooks Moses
+13  A: 
liori
These solutions are way better than mine, if they're allowed.
Steve Jessop
ah gamma. A constant close to my heart!
Mitch Wheat
ugh, this is two answers in one post. upvote is for the gamma-containing formula. the log(exp...) thing isn't going to help, as it has the same problem of losing bits while accumulating many values.
DarenW
A: 

As all your numbers are rationals, the easiest (and also maybe the fastest, as it will have to do less floating point operations) would be to do the computations with rationals (tuples of 2 integers p,q), and then do just one floating point division at the end.

update to use this technique effectively you will need to use bigints for p & q, as they grow quite fast...

A fast prototype in Lisp, that has built in rationals shows:

(defun sum_harmonic (n acc)
  (if (= n 0) acc (sum_harmonic (- n 1) (+ acc (/ 1 n)))))

(sum_harmonic 10 0)
7381/2520
[2.9289682]

(sum_harmonic 100 0)
14466636279520351160221518043104131447711/278881500918849908658135235741249214272
[5.1873775]

(sum_harmonic 1000 0)

53362913282294785045591045624042980409652472280384260097101349248456268889497101
75750609790198503569140908873155046809837844217211788500946430234432656602250210
02784256328520814055449412104425101426727702947747127089179639677796104532246924
26866468888281582071984897105110796873249319155529397017508931564519976085734473
01418328401172441228064907430770373668317005580029365923508858936023528585280816
0759574737836655413175508131522517/712886527466509305316638415571427292066835886
18858930404520019911543240875811114994764441519138715869117178170195752565129802
64067621009251465871004305131072686268143200196609974862745937188343705015434452
52373974529896314567498212823695623282379401106880926231770886197954079124775455
80493264757378299233527517967352480424636380511370343312147817468508784534856780
21888075373249921995672056932029099390891687487672697950931603520000
[7.485471]

So, the next better option could be to mantain the list of floating points and to reduce it summing the two smallest numbers in each step...

fortran
That could be tricky. I think you'd need to make the denominator the product of all numbers 1 through n in order to be able to add them properly. That means n! which would run out of 32-bit int very quickly.
paxdiablo
You haven't tried that, have you? The denominator grows very quickly, it is n! for given n. You'd have to use some biginteger implementation.
liori
that's true... I thinking that maybe there were common divisors along the way that could simplify the fraction... I'm going to try it in lisp that has fractionals built in.
fortran
+4  A: 

I don't even know where to begin.

Here: What Every Computer Scientist Should Know About Floating-Point Arithmetic

It would have been more helpful to link directly to the most relevant section of that article, namely the section on optimizers and the Kahan Summation Formula: http://docs.sun.com/source/806-3568/ncg_goldberg.html#1070
las3rjock
+5  A: 

Actually, if you're doing the summation for large N, adding in order from smallest to largest is not the best way -- you can still get into a situation where the numbers you're adding are too small relative to the sum to produce an accurate result.

Look at the problem this way: You have N summations, regardless of ordering, and you wish to have the least total error. Thus, you should be able to get the least total error by minimizing the error of each summation -- and you minimize the error in a summation by adding values as nearly close to each other as possible. I believe that following that chain of logic gives you a binary tree of partial sums:

Sum[0,i] = value[i]

Sum[1,i/2] = Sum[0,i] + Sum[0,i+1]

Sum[j+1,i/2] = Sum[j,i] + Sum[j,i+1]

and so on until you get to a single answer.

Of course, when N is not a power of two, you'll end up with leftovers at each stage, which you need to carry over into the summations at the next stage.

(The margins of StackOverflow are of course too small to include a proof that this is optimal. In part because I haven't taken the time to prove it. But it does work for any N, however large, as all of the additions are adding values of nearly identical magnitude. Well, all but log(N) of them in the worst not-power-of-2 case, and that's vanishingly small compared to N.)

Brooks Moses
Seems logical.
liori
+1  A: 

Unless you use some accurate closed-form representation, a small-to-large ordered summation is likely to be most accurate simple solution (it's not clear to me why a log-exp would help - that's a neat trick, but you're not winning anything with it here, as far as I can tell).

You can further gain precision by realizing that after a while, the sum will become "quantized": Effectively, when you have 2 digits of precision, adding 1.3 to 41 results in 42, not 42.3 - but you achieve almost a precision doubling by maintaining an "error" term. This is called Kahan Summation. You'd compute the error term (42-41-1.3 == -0.3) and correct that in the next addition by adding 0.3 to the next term before you add it in again.

Kahan Summation in addition to a small-to-large ordering is liable to be as accurate as you'll ever need to get. I seriously doubt you'll ever need anything better for the harmonic series - after all, even after 2^45 iterations (crazy many) you'd still only be dealing with a numbers that are at least 1/2^45 large, and a sum that's on the order of 45 (<2^6), for an order of magnitude difference of 51 powers-of-two - i.e. even still representable in a double precision variable if you add in the "wrong" order.

If you go small-to-large, and use Kahan Summation, the sun's probably going to extinguish before today's processors reach a percent of error - and you'll run into other tricky accuracy issues just due to the individual term error on that scale first anyhow (being that a number of the order of 2^53 or larger cannot be represented accurately as a double at all anyhow.)

Eamon Nerbonne
(Note that you might need to be careful with optimization settings if you use Kahan Summation, as a compiler might otherwise rewrite it into oblivion).
Eamon Nerbonne