Help w/ Floats needed!

S

Somebody

Hi guys... quick question about floats...

I have a point "int x", and a width "int cx". In order to support scaling, I
store this as "double ratio = x / cx" which invariably led to round off
errors when I tried to re-calculate the new position from the ratio as in:

x = ratio * cx;

I tried to solve this by multiplying the numerator by 10,000 to gain more
precision... recent testing showed that I had not solved the problem in all
cases. Thinking about it, no matter how many times I multiply the numerator
of a fraction, I'll never be able to recover 100 from 1/3 (0.333333....) for
example.

I came up with this idea: instead of storing "double ratio = x / cx", I will
store int numerator=x, int denominator = cx;

That way, in my case off 1/3, I'll be able to fully recover the original
value. Ie...:

original pos = 33
original width = 100

num = 33;
denom = 100;

new width = num * 100 / denom = 33;

Does this sound like a reasonable solution (with ABSOLUTELY ZERO LOSS OF
PRECISION)? Or is there a better way?

Thanks!
 
J

Jim Langston

Somebody said:
Hi guys... quick question about floats...

I have a point "int x", and a width "int cx". In order to support
scaling, I store this as "double ratio = x / cx" which invariably led

This might be your problem right here.

double ratio = x / cx;
is going to do interger math because x is an interger and cx is an integer.

Proof of this is in a small test program which outputs
0

#include <iostream>

int main()
{
int x = 1;
int y = 3;
double z = x / y;

std::cout << z << "\n";
}

So, what to do? Cast x to a double.
Change the line in the program to:
double z = static_cast<double>( x ) / y;
and the output is
0.333333

to round off errors when I tried to re-calculate the new position
from the ratio as in:
x = ratio * cx;
[SNIP]
 
J

James Kanze

I have a point "int x", and a width "int cx". In order to
support scaling, I store this as "double ratio = x / cx" which
invariably led to round off errors when I tried to
re-calculate the new position from the ratio as in:
x = ratio * cx;
I tried to solve this by multiplying the numerator by 10,000
to gain more precision... recent testing showed that I had not
solved the problem in all cases. Thinking about it, no matter
how many times I multiply the numerator of a fraction, I'll
never be able to recover 100 from 1/3 (0.333333....) for
example.
No.

I came up with this idea: instead of storing "double ratio = x
/ cx", I will store int numerator=x, int denominator = cx;
That way, in my case off 1/3, I'll be able to fully recover the original
value. Ie...:

Not unless your doubles use a base 3, or a power of 3.
original pos = 33
original width = 100
num = 33;
denom = 100;
new width = num * 100 / denom = 33;
Does this sound like a reasonable solution (with ABSOLUTELY
ZERO LOSS OF PRECISION)?

You haven't really specified the problem. Zero loss of
precision isn't possible with a finite representation, so you
can forget about that.
 
K

Kai-Uwe Bux

James said:
Not unless your doubles use a base 3, or a power of 3.





You haven't really specified the problem. Zero loss of
precision isn't possible with a finite representation, so you
can forget about that.

Well, the OP does not want to store arbitrary real numbers. All the
rescaling factors he wants to store arise as ratios of ints. In that case,
storing the two ints does represent the ratio without loss of precision.


Best

Kai-Uwe Bux
 
S

Somebody

So, what to do? Cast x to a double.
Change the line in the program to:
double z = static_cast<double>( x ) / y;
and the output is
0.333333

I know that :)... I was only including psuedo-code... but the point is:

double x = 100;
double cx = 300;
double ratio = x / cx; // = 0.33333

double newwidth = 300;
double newpos = ratio * newwidth; // result = 99, NOT 100 which is what it
should be.

My solution of storing the numerator and denomenator to get a real scaling
factor worked.
 
S

Somebody

That way, in my case off 1/3, I'll be able to fully recover the original
value. Ie...:
Not unless your doubles use a base 3, or a power of 3.

Huh? What does this have to do with 3's?

My point was, in the case of a 1/3 ratio (or any ratio with an infinite
repeating decimal), I'll never be able to recalculate the numerator exactly
given the denomenator and ratio.

But by storing the numerator and denomenator as ints instead of a ratio, I
would.
 
S

Somebody

Well, the OP does not want to store arbitrary real numbers. All the
rescaling factors he wants to store arise as ratios of ints. In that case,
storing the two ints does represent the ratio without loss of precision.


Best

Kai-Uwe Bux

Thanks Kai-Uwe. Seems like you are the only one who understood the question
:).
 
P

Paul Brettschneider

Somebody said:
I know that :)... I was only including psuedo-code... but the point is:

double x = 100;
double cx = 300;
double ratio = x / cx; // = 0.33333

double newwidth = 300;
double newpos = ratio * newwidth; // result = 99, NOT 100 which is what it
should be.

Beg your pardon?

#include <iostream>

int main()
{
double x = 100;
double cx = 300;
double ratio = x / cx;

double newwidth = 300;
double newpos = ratio * newwidth;

std::cout << newpos << std::endl;
}

gives 100 for me. Of course the internal representation might correspond to
99.99999 or something like that but *definitely* not something closer to 99
than to 100.
 
J

James Kanze

I know that :)... I was only including psuedo-code... but the point is:
double x = 100;
double cx = 300;
double ratio = x / cx; // = 0.33333
double newwidth = 300;
double newpos = ratio * newwidth; // result = 99, NOT 100 which is what it
should be.

Are you kidding? The result won't be 100.0, but it will
be fairly close to 100. (In fact, it might actually end up as
100.0; it does on my machine, anyway. But of course, you can't
count on it; you can only count on it being fairly close.)
My solution of storing the numerator and denomenator to get a
real scaling factor worked.

It's guaranteed to be 100% accurate. And very slow, and liable
to overflow in integral arithmetic. Depending on the
application, it might be what's needed, or it might not be.
 
J

James Kanze

Huh? What does this have to do with 3's?

The fraction 1/3 will only be exactly representable in a double
if the base of the double is 3 or a multiple of 3.
My point was, in the case of a 1/3 ratio (or any ratio with an
infinite repeating decimal), I'll never be able to recalculate
the numerator exactly given the denomenator and ratio.
But by storing the numerator and denomenator as ints instead
of a ratio, I would.

That's one solution. If the target value is an int, after
rescaling, it's probable that the double will store the ratio
with enough precision, provided that you round the results
correctly.
 
S

Somebody

Are you kidding? The result won't be 100.0, but it will
be fairly close to 100. (In fact, it might actually end up as
100.0; it does on my machine, anyway. But of course, you can't
count on it; you can only count on it being fairly close.)

Fairly close to 100 is not 100, now is it? :). In fact, when I convert it to
an int, it would get truncated down to 99.

Imagine this scenario to explain why I needed it EXACT. You have an object
on the screen at x,y = 100,100. You do something on the screen that causes
the layout to be recalculated and suddenly the object moves to 99,99. Not
very good.
 
S

Somebody

gives 100 for me. Of course the internal representation might correspond
to
99.99999 or something like that but *definitely* not something closer to
99
than to 100.

Hmm... so it does for me too (in a test app). I was definitely seeing round
off errors in my layout when I was using doubles. Possible I might have had
that because of the way I did the casting throughout the algorithm. Oh well,
its working with the int pair solution. No point in going back to try to
figure it out.
 
T

Thomas J. Gritzan

Somebody said:
Fairly close to 100 is not 100, now is it? :). In fact, when I convert it to
an int, it would get truncated down to 99.

Imagine this scenario to explain why I needed it EXACT. You have an object
on the screen at x,y = 100,100. You do something on the screen that causes
the layout to be recalculated and suddenly the object moves to 99,99. Not
very good.

You could try rounding the double to nearest integer when converting to int:

int roundToNearest(double d)
{
return static_cast<int>(d + 0.5);
}

This will round values in the range [98.5, 99.5) to 99 and [99.5, 100.5)
to 100. Simple casting to int will round the value down.
 
J

James Kanze

Fairly close to 100 is not 100, now is it? :). In fact, when I
convert it to an int, it would get truncated down to 99.

If you want it rounded to a certain precision, you round it to
that precision. If you round the value to an int, you'll get
100, and not 99.
Imagine this scenario to explain why I needed it EXACT. You
have an object on the screen at x,y = 100,100. You do
something on the screen that causes the layout to be
recalculated and suddenly the object moves to 99,99. Not very
good.

So round correctly when you need to determine the pixel.
 
F

Frank Birbacher

Hi!
My point was, in the case of a 1/3 ratio (or any ratio with an infinite
repeating decimal), I'll never be able to recalculate the numerator exactly
given the denomenator and ratio.

Although your statement is logically correct the reverse it not:

Not every finite decimal (that is no repeating decimal) is exactly
representable as a float/double.

The point is: you must not forget that doubles are 2-based and not
10-based. That's why a number like 0.3 cannot be exactly stored in a
double, because it has an infinitly repeating binary representation. But
0.25 can (one fourth, or 0.01 in binary, or 1e-2 in binary).

You must learn that floating point calculations are not exact. You must
learn there is no operator == in floating point math. For your problem
either use proper rounding or fractional numbers (as you proposed).

Regards,
Frank
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top