I need help regarding fraction to string

closed account (9y8C5Di1)

double b=0.34;int c=0;

for(;b>0;b*=10,c=b){
cout<<b<<"-"<<c<<"="<<b-c<<endl;

b=b-c;
}

It is for converting a fraction part of a number to a string.

So i multiply the fraction with 10 and stores the result in an integer, which cannot hold any fractions so therefore i only get the decimal. Then i substract the obtained integer from the fraction part because it is no longer needed.

The problem is that on the last substraction let's say 4-4. It comes up with a complete different number, example it says that 4-4=9.2321321e01.

But what i find strangest is that it only calculates wrong on the very last fraction part, not the preceeding ones.

Example:

0.34
3.4
3-3.4=0.4

0.4
4
4-4=9.3232e3
Last edited on
make int c on line 1 into double c
closed account (9y8C5Di1)
The whole point is that an integer cannot store a fraction part.
So if i have a number 1.3 and i assign that number to an integer variable
it will then only contain 1 and not 1.3. So by then substracting 1 from 1.3 i will be returned 0.3. So i can then assign some string the integer number + 48, to achieve the actual fraction part.

It does this and works correctly until the last substraction.. Then it just returns an unrealistic number. In this example it would return: 3-3=not 0.




what about trying c-b instead of b-c
closed account (9y8C5Di1)
Then c will be negative the point value of b, because c is equal to b.
And i don't want it to be negative.
Last edited on
This sounds a lot like my convert float to string function, which was derived from a convert int to char function. All meant to make output easier, turned out to be a lot of work.

I used a modulus operation to find the remainder of a division operation like this:

where number = input;

for each tens place starting with the largest
*use a find #of digits function to return the number of iterations needed

int digits = findDigits(number);

char *output = new char[digits]; // create a string with enough places

//starting with the highest tens place
for(int i=digits; i>0; i--)
{
int next_highest_tens_place = pow(10, i+1);
int current_tens_place = pow(10, i);

int digit = (next_highest_tens_place % number)-(current_tens_place%number) / current_tens_place;

output[digits-1] = int('0' + digit);

};

return output;

that would return an integer number to a char string,
and if you added a bit about where to place a decimal, should work for that as well
Last edited on
1
2
3
4
5
6
7
8
#include <cmath>
#include <string>

std::string frac_part_to_string( double value )
{
    double int_part ;
    return std::to_string(  std::modf( value, &int_part ) ) ;
}


Or C++98:
1
2
3
4
5
6
7
8
9
#include <sstream>

std::string frac_part_to_string( double value )
{
    std::ostringstream stm ;
    double int_part ;
    stm << std::fixed << std::modf( value, &int_part ) ;
    return stm.str() ;
}


A simple implementation of modf():
http://ftp.samba.org/pub/tridge/misc/modf.c
closed account (9y8C5Di1)
I try to avoid using predefined functions.
That is why i didnt.
Last edited on

0.34
3.4
3-3.4=0.4


1
2
3
double a = 0.34;
double b = a * 10.0;
double c = (int)(a * 10) - b;
Last edited on
closed account (9y8C5Di1)
That might work because it's not extracting an integer from a double, thanks.
closed account (9y8C5Di1)
However, why it is only returning unreasonable values and only after having passed the last number to add, is still a bit strange. Because if it did cause some disturbance to switch between types like that, it should do it no matter what.

Does anyone have an explanation?
Please post some number examples here.
closed account (9y8C5Di1)
double t=0.45;
for(int y=0;t>0;t*=10,y=t){of<<t<<"-"<<y<<"="<<t-y<<"\n";
elem2+=y+48;t=t-y;
}

Here are the number outputs for the fraction 0.45.

Notice how on the second substraction it is all calculated correctly, but on the third substraction(which is the last substraction) it is totally wrong.

0.45-0=0.45
4.5-4=0.5
5-5=4.54747e-012
4.54747e-011-0=4.54747e-011
4.54747e-010-0=4.54747e-010
4.54747e-009-0=4.54747e-009
4.54747e-008-0=4.54747e-008
4.54747e-007-0=4.54747e-007
4.54747e-006-0=4.54747e-006
4.54747e-005-0=4.54747e-005
0.000454747-0=0.000454747
0.00454747-0=0.00454747
0.0454747-0=0.0454747
0.454747-0=0.454747
4.54747-4=0.547474
5.47474-5=0.474735
4.74735-4=0.747351
7.47351-7=0.473509
4.73509-4=0.735089
7.35089-7=0.350886
3.50886-3=0.508865
5.08865-5=0.0886464
0.886464-0=0.886464
8.86464-8=0.864641
8.64641-8=0.646412
6.46412-6=0.464119
4.64119-4=0.64119
6.4119-6=0.411896
4.11896-4=0.118958
1.18958-1=0.189575
1.89575-1=0.895752
8.95752-8=0.95752
9.5752-9=0.575195
5.75195-5=0.751953
7.51953-7=0.519531
5.19531-5=0.195313
1.95313-1=0.953125
9.53125-9=0.53125
5.3125-5=0.3125
3.125-3=0.125
1.25-1=0.25
2.5-2=0.5
5-5=0


Here is another one for the fraction: 0.453767.
It follows the exact same pattern, and it does not calculate wrong until the last substraction. However now i noticed that there is most definetly some left over fractions because it should be 7-7 and not 7-6. But still the strangest thing remains; why does this only occur on the last element to substract?


0.453767-0=0.453767
4.53767-4=0.53767
5.3767-5=0.3767
3.767-3=0.767
7.67-7=0.67
6.7-6=0.7
7-6=1
10-9=1
10-9=0.999997
9.99997-9=0.999971
9.99971-9=0.999707
9.99707-9=0.997072
9.97072-9=0.970722
9.70722-9=0.707215
7.07215-7=0.0721547
0.721547-0=0.721547
7.21547-7=0.215466
2.15466-2=0.154656
1.54656-1=0.546561
5.46561-5=0.465605
4.65605-4=0.656053
6.56053-6=0.560526
5.60526-5=0.605259
6.05259-6=0.0525894
0.525894-0=0.525894
5.25894-5=0.258942
2.58942-2=0.589417
5.89417-5=0.894165
8.94165-8=0.94165
9.4165-9=0.416504
4.16504-4=0.165039
1.65039-1=0.650391
6.50391-6=0.503906
5.03906-5=0.0390625
0.390625-0=0.390625
3.90625-3=0.90625
9.0625-9=0.0625
0.625-0=0.625
6.25-6=0.25
2.5-2=0.5
5-5=0
Last edited on
Welcome in the wonderful world of floating point arithmetic :)

Your problem comes from the fact that the numbers you enter can not be exactly represented.
The error is extremely small, but since you're comparing to 0 it counts.

To correct that, you MUST define a value under which any number is considered as a roundoff error.

Then, you have 2 possibilities:
1) You round b to the wanted precision
1
2
3
4
5
6
7
8
    double b=0.453767;int c=0;
    double EPS = 1e-10;

    for(;b>0;b=EPS*round(10*b/EPS),c=b)
    {
        std::cout<<b<<"-"<<c<<"="<<b-c<<std::endl;
        b=b-c;
    }


2) You take the error into account when looping and computing c
1
2
3
4
5
6
7
8
    double b=0.453767;int c=0;
    double EPS = 1e-10;

    for(;b>EPS;b*=10,c=b+EPS)
    {
        std::cout<<b<<"-"<<c<<"="<<b-c<<std::endl;
        b=b-c;
    }


Method 1) requires using a rounding function and knowing the maximum precision you'll need to determine EPS.
Method 2) does not require additional code but you still have to determine EPS.
closed account (9y8C5Di1)
Can you explain more about this rounding concept and EPS?
If you read about floating point arithmetic, you'll soon find that using equality or inequality tests to compare two floating point values is strongly discouraged.
This is because floating point values have a high precision, and two values that would seem equal to us may not be exactly equals.
So you have to resort to things like this
 
if( fabs(fp1 - fp2) <= EPS ) // instead of if( fp1 == fp2 ) 

where EPS is the maximal distance under which you consider two numbers to be equal.

In your case, you have two values : the one you think you entered, and the one that is really stored, which is slightly off.
EPS is a kind of upper bound of the distance between the 2 values.

As for the rounding, it's simple :
If you have two numbers x and y, computing y*round(x/y) gives you the multiple of y nearest to x.

Let's choose an EPS that divides the wanted value of b.
Computing EPS*round(wanted_value/EPS) will give wanted_value.

Computing EPS*round(stored_value/EPS) will give the multiple of EPS nearest to stored_value.
If the distance between the wanted value and the stored value is small enough compared to EPS (ie smaller than EPS/2), the nearest multiple of EPS will be the wanted value.
So, by rounding, we eliminated the error.

My choice of EPS:
Since your value is a finite decimal number, I figured that a sufficiently small power of 10 would always divide it.
In your examples, the error was always around 1e-12.
Taking 1e-10 seemed a good choice.

Your choice of EPS:
The trick with floating point values is that the error is proportional to the value stored.
The best choice of EPS would be to compute it proportional to b.
For example, if you know you will not have more than N significant digits in your number, you could try something like EPS = b*1e-(N+1);
The 2nd method is easier to understand.
As you can see in your examples, you can be slightly above or slightly under the intended value.

When you're slightly above, looping only if b > EPS guarantees your loop will stop when what remains is smaller than EPS.

When you're slightly under, the problem is the computation of c.
Let's say the integer value you want is iVal. What is stored is iVal-error.
When you compute c, you basically compute floor(iVal-error) which is iVal-1.
By adding EPS, as long as the error is smaller than EPS, EPS-error > 0 and floor( iVal+EPS-error) equals iVal.
closed account (9y8C5Di1)
Thank you for explaining. I think this is all the information needed.
Topic archived. No new replies allowed.