Reputation: 818
I'm trying to make a simple function that count the number of digits. I have some written some function that work perfectly but I'm stuck on this one :
// The number I want to count the digits after the decimal
long double d=33.56;
// Going to modify d but I wan't to keep the original value
long double dTemp=d;
// v to store the part after the decimal and e the part before
long double v, e;
v=modfl(dTemp,&e);
// j is the number of digit after the decimal
int j=0;
while(dTemp!=e)
{
j++;
dTemp*=10;
v=modfl(dTemp,&e);
std::cout<<"D = "<<dTemp<<" e = "<<e<<" v = "<<v<<std::endl;
}
std::cout<<"J = "<<j<<std::endl;
The output is :
D = 335.6 e = 335 v = 0.6
D = 3356 e = 3356 v = 2.27374e-13
D = 33560 e = 33560 v = 2.27374e-12
D = 335600 e = 335600 v = 2.27374e-11
D = 3.356e+06 e = 3.356e+06 v = 2.27374e-10
D = 3.356e+07 e = 3.356e+07 v = 2.27374e-09
D = 3.356e+08 e = 3.356e+08 v = 2.27301e-08
D = 3.356e+09 e = 3.356e+09 v = 2.27243e-07
D = 3.356e+10 e = 3.356e+10 v = 2.27243e-06
D = 3.356e+11 e = 3.356e+11 v = 2.27094e-05
D = 3.356e+12 e = 3.356e+12 v = 0.000226974
D = 3.356e+13 e = 3.356e+13 v = 0.00226974
D = 3.356e+14 e = 3.356e+14 v = 0.0227051
D = 3.356e+15 e = 3.356e+15 v = 0.227051
D = 3.356e+16 e = 3.356e+16 v = 0.269531
D = 3.356e+17 e = 3.356e+17 v = 0.6875
D = 3.356e+18 e = 3.356e+18 v = 0
J = 17
But, if you look at line 2 of the output, you have :
D = 3356 e = 3356 v = 2.27374e-1
So, dTemp is equal to e and the while loop still continues.
What I tried :
I though I could be due to d not being able to store a number as big as needed. This is why I used a long double
Upvotes: 0
Views: 109
Reputation: 7610
It is not a good practice to check equation/not equation of float/double/long double numbers directly. So I suggest to use something like this
Not equal:
while (abs(dTemp - e) > 1e-12) { ... }
Equal:
while (abs(dTemp - e) < 1e-12) { ... }
The "tiny" number depends on the type of the not-integer number (floating point or fixed precision real). M-Gregoire's comment about using std::numeric_limits<double>::epsilon()
may sound good, but one may face to a problem again. As 33.56 cannot be calculated as a finite sum of positive and/or negative powers of 2, so it cannot be stored! Always there are itsy-bitsy differences. Internally it is stored like this: 33.56000000000000227373675443232059478759766
(I printed in perl, but in c++ you get similar result). So you may set this "tiny" difference value to a "proper" value, which is high enough to disable this internal float format problem.
You may use another approach. An std::ostringstream
could be used with manipulator setprecision
setting the precision to a "proper" number to convert the number to string and then count the digits in the string. But it as also not that simple.
I checked the value '33.56' in c++ as well. See the example code:
#include <iostream>
#include <iomanip>
int main() {
float df = 33.56;
double dd = 33.56;
long double dld = 33.56;
std::cout << std::setprecision(50) << df << std::endl;
std::cout << std::setprecision(50) << dd << std::endl;
std::cout << std::setprecision(50) << dld << std::endl;
}
The output is:
33.560001373291015625
33.56000000000000227373675443232059478759766
33.56000000000000227373675443232059478759766
So in case of float 1e-5 could be used for the gap value, for double 1e-14 seems to be the right value in this case.
Upvotes: 3