Pathological floating point problems: Difference between revisions

Content deleted Content added
Aamrun (talk | contribs)
Added C implementation for first two tasks.
Line 504:
With 256 bits of precision, tasks 2 and 3 provide the same answer as above. Task 1 appears to be converging after 50 iterations, but by 100 iterations the answer has changed to 100.0
 
=={{header|C}}==
Such exercises are very good examples that just because you have a nice library doesn't mean you won't get the wrong results. I was over-ambitious and the result is I have been wrangling with the Chaotic Bank task for a long time now, I will come back to it later but for now here are the first two cases, the "trivial" one of 0.1 + 0.2 and the Pathological series :
===First two tasks===
<lang C>
/*Abhishek Ghosh, 10th November 2017*/
 
#include<stdio.h>
#include<gmp.h>
 
void firstCase(){
mpf_t a,b,c;
mpf_inits(a,b,c,NULL);
mpf_set_str(a,"0.1",10);
mpf_set_str(b,"0.2",10);
mpf_add(c,a,b);
gmp_printf("\n0.1 + 0.2 = %.*Ff",20,c);
}
 
void pathologicalSeries(){
int n;
mpf_t v1, v2, vn, a1, a2, a3, t2, t3, prod;
mpf_inits(v1,v2,vn, a1, a2, a3, t2, t3, prod,NULL);
mpf_set_str(v1,"2",10);
mpf_set_str(v2,"-4",10);
mpf_set_str(a1,"111",10);
mpf_set_str(a2,"1130",10);
mpf_set_str(a3,"3000",10);
for(n=3;n<=100;n++){
mpf_div(t2,a2,v2);
mpf_mul(prod,v1,v2);
mpf_div(t3,a3,prod);
mpf_add(vn,a1,t3);
mpf_sub(vn,vn,t2);
if((n>=3&&n<=8) || n==20 || n==30 || n==50 || n==100){
gmp_printf("\nv_%d : %.*Ff",n,(n==3)?1:(n>=4&&n<=7)?6:(n==8)?7:(n==20)?16:(n==30)?24:(n==50)?40:78,vn);
}
mpf_set(v1,v2);
mpf_set(v2,vn);
}
}
 
void healthySeries(){
int n;
mpf_t num,denom,result;
mpq_t v1, v2, vn, a1, a2, a3, t2, t3, prod;
mpf_inits(num,denom,result,NULL);
mpq_inits(v1,v2,vn, a1, a2, a3, t2, t3, prod,NULL);
mpq_set_str(v1,"2",10);
mpq_set_str(v2,"-4",10);
mpq_set_str(a1,"111",10);
mpq_set_str(a2,"1130",10);
mpq_set_str(a3,"3000",10);
for(n=3;n<=100;n++){
mpq_div(t2,a2,v2);
mpq_mul(prod,v1,v2);
mpq_div(t3,a3,prod);
mpq_add(vn,a1,t3);
mpq_sub(vn,vn,t2);
if((n>=3&&n<=8) || n==20 || n==30 || n==50 || n==100){
mpf_set_z(num,mpq_numref(vn));
mpf_set_z(denom,mpq_denref(vn));
mpf_div(result,num,denom);
 
gmp_printf("\nv_%d : %.*Ff",n,(n==3)?1:(n>=4&&n<=7)?6:(n==8)?7:(n==20)?16:(n==30)?24:(n==50)?40:78,result);
}
mpq_set(v1,v2);
mpq_set(v2,vn);
}
}
 
int main()
{
mpz_t rangeProd;
firstCase();
printf("\n\nPathological Series : ");
pathologicalSeries();
printf("\n\nNow a bit healthier : ");
healthySeries();
 
return 0;
}
</lang>
The reason I included the trivial case was the discovery that the value of 0.3 is stored inexactly even by GMP if 0.1 and 0.2 are set via the mpf_set_d function, a point observed also during the solution of the [Currency] task. Thus mpf_set_str has been used to set the values, for the 2nd task, a great learning was not to convert the values into floating points unless they have to be printed out. It's a polynomial with out a single floating point coefficient or exponent, a clear sign that the values must be treated as rationals for the highest accuracy. Thus there are two implementations for this task in the above code, one uses floating points, the other rationals. There is still a loss of accuracy even when floating points are used, probably during the conversion of a rational to a float.
<pre>
0.1 + 0.2 = 0.30000000000000000000
 
Pathological Series :
v_3 : 18.5
v_4 : 9.378378
v_5 : 7.801153
v_6 : 7.154414
v_7 : 6.806785
v_8 : 6.5926328
v_20 : 6.0751649921786439
v_30 : 99.999999824974113455900000
v_50 : 100.0000000000000000000000000000000000000000
v_100 : 100.000000000000000000000000000000000000000000000000000000000000000000000000000000
 
Now a bit healthier :
v_3 : 18.5
v_4 : 9.378378
v_5 : 7.801153
v_6 : 7.154414
v_7 : 6.806785
v_8 : 6.5926328
v_20 : 6.0435521101892689
v_30 : 6.006786093031205758530000
v_50 : 6.0001758466271871889100000000000000000000
v_100 : 6.000000019319477929060000000000000000000000000000000000000000000000000000000000
</pre>
=={{header|Excel}}==
{{works with|Excel 2003|Excel 2015}}