Working with floats, adding precision

Pages: 12
Oct 16, 2009 at 10:00pm
Here's my code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
float a=1.23456;

float b=a;
char ch[7]="";
int i;
int count=0;
int precision=1;

do{
  if(b<1){ b*=10; precision--; } //For 0.### numbers
  while(b>10){ b/=10; precision++; }

  //Change to char
  i=b;
  ch[count]=i+'0';
    count++;
    cout<<b<<" - ";  //debug
    b-=i;
    cout<<b<<" - ";  //debug
    b*=10;
    cout<<b<<endl;  //debug
}while(count<6);


My problem is that as B is deducted from, new numbers start adding into the old places. Here is a simple run

lined up for ease
1
2
3
4
5
6
1.23456 - 0.23456    - 2.3456
2.3456  - 0.3456     - 3.456
3.456   - 0.456001   - 4.56001
4.56001 - 0.560013   - 5.60013
5.60013 - 0.600128   - 6.00128
6.00128 - 0.00128174 - 0.0128174


As you see, new numbers start coming into the float, fiving me a problem is a smaller number is used.

So my question is this. either how can I stop numbers besides 0 coming in?
Is there some math formula I can use to chage the new non-zero number to zero?

Thanks.
Last edited on Oct 16, 2009 at 10:02pm
Oct 16, 2009 at 10:45pm
how can I stop numbers besides 0 coming in?
You can't.

This is a common misconception people have about floating point numbers.
The number 1.23456 is in decimal, but floats are in binary. 1.23456 can't be precisely represented in binary, only approximately. The numbers you see coming from the right are the error between the binary and the decimal representation.
There's a few methods you can use to fix this. From best to worse:
1. Use a better conversion algorithm. I believe the standard method is to actually perform the operation described by the floating point representation, in decimal.
2. Use a double. This may not actually solve it.
3. Multiply by 10^5 and assign to an integer.
Oct 17, 2009 at 12:02am
Useing doubles worked!

I've not got a problem that is really confusing to me.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
double a=1.23;
double b=a;
char ch[7]="";
int i;
int count=0;
int precision=1;

do{
  if(b<1){ b*=10; precision--; } //For 0.### numbers
  while(b>10){ b/=10; precision++; }

  //Change to char
  cout<<i<<" - "<<b;
  i=b;
  cout<<" - "<<i<<endl;
  ch[count]=i+'0';
  if(b==i)break;

  count++;
  b-=i;
  b*=10;
}while(count<6);


This gives the output

1
2
3
4
5
6
0 - 1.23 - 1
1 - 2.3   - 2
2 - 3      - 2  //<<<<<Problem
2 - 10    - 9
9 - 10    - 9
9 - 10    - 9


Where I pointed out the problem, it should be 2 - 3 - 3, however my statement i=b seems to be disregarded the third time through, therefore not allowing my if statment to stop the loop.

Any clues?
Oct 17, 2009 at 12:10am
2.23 is represented as exactly 2.23-epsilon. See solutions 1 and 3 above.
Oct 17, 2009 at 12:15am
Sorry, I may be just missing something here, but I don't see how those will help.

It should be that I is assinged B's value, which is currently 3. But as you see from my run, that I = 2 before, and after my i=b; statement. However, on others, such as line 2, I goes from 1 to 2, as expected when given the value 2.3.

So am I missing something, or was I not clear in my last post? (sorry if it is either)
Oct 17, 2009 at 12:48am
2.23 is actually being represented as 2.229999... By the third step, b is 2.9999..., which is rounded and printed as 3. If you first <<std::setprecision(20) (#include <iomanip>), you'll see what I mean.
There's really no way around this, other than method #1 I described above, or using sprintf() or std::stringstream.
Oct 17, 2009 at 4:04am
Well, I guess I'll be going with method one sometime in the future.

Thanks a ton for your help!

Before I mark this thread as solved, can you tell me what file sprintf/snprintf() comes from? Also, is it a C function? (I ask because of the printf part)
Oct 17, 2009 at 4:17am
<cstdio> and yes.
Oct 17, 2009 at 4:19am
Strange, I'm not including that in my program.

Okay, too bad there isn't a C++ equivilent.

Thanks again!
Oct 17, 2009 at 5:49am
Did you not notice my mention of std::stringstream?
Oct 17, 2009 at 5:55am
Yes. But isn't that for converting strings to streams, not visa versa?
Oct 17, 2009 at 7:39am
Just like you can send integers, floats, etc. to std::cout using the << operator, you can send the same things to an std::stringstream and get the contents using std::stringstream::str().
Oct 17, 2009 at 10:18am
There are also rounding algorithms to take a number like 2.999 and round it to the nearest integer value (3). See the following link for some reading and some rounding algorithms:
http://www.cplusplus.com/forum/articles/3638/
Good luck!
Oct 17, 2009 at 3:58pm
Okay, I'll look into that some more then.

Thanks for the article. I actually made myself a simple function that rounds up or down, depending on the number

1
2
3
4
5
float round(float &f){
  if(ceil(f)-f <= f+floor(f)-ceil(f)) return ceil(f);
  else if(ceil(f)-f > f+floor(f)-ceil(f)) return floor(f);
  return -1;
}

I also overloaded it for doubles. What do you think?
Oct 17, 2009 at 5:54pm
That's supposed to be better than:
1
2
3
4
5
6
7
8
  //--------------------------------------------------------------------------
  // Common rounding: round half up
  // Bias: +Infinity
  template <typename FloatType>
  FloatType roundhalfup( const FloatType& value )
    {
    return std::floor( value +0.5 );
    }
?
Oct 17, 2009 at 6:02pm
The functions are different. roundhalfup simply adds 0.5 to your value.

Mine, determins wheter the number is .5+ and if so rounds up, and if it's not then it rounds down.

Looking at it, I've changed it to.

1
2
3
4
5
int round(float f){
  if(ceil(f)-f <= f+floor(f)-ceil(f)) return ceil(f);
  else return floor(f);
  return -1;
}


A little simpler.

See the difference?
Oct 17, 2009 at 6:41pm
Can you provide an example where the two functions will return different values given the same input?
Oct 17, 2009 at 6:51pm
Given the same input, they will return the same.

But here is an example.

1.5 = 2
1.4 = 1
1.5254 = 2
1.4999 = 1

As you see from the last example, it does't work precisely, as the last one should round up to 2.
Still, as long as it's .5+ it rounds up, if not it rounds down.

Working to get it to round correctly. Any ideas?
Oct 17, 2009 at 6:58pm
You don't have any clue what you are talking about, do you?
I'm going away.
Oct 17, 2009 at 7:13pm
Well, I thought I did. But if I don't, please help me to understand. Unless you just don't want to help, then there is no need for you to respond to this post.
Pages: 12