Help w/ Floats needed!

Discussion in 'C++' started by Somebody, May 18, 2008.

  1. Somebody

    Somebody Guest

    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!
     
    Somebody, May 18, 2008
    #1
    1. Advertising

  2. Somebody

    Jim Langston Guest

    Somebody wrote:
    > 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]

    --
    Jim Langston
     
    Jim Langston, May 18, 2008
    #2
    1. Advertising

  3. Somebody

    James Kanze Guest

    On 18 mai, 06:43, "Somebody" <> wrote:
    > 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.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, May 18, 2008
    #3
  4. Somebody

    Kai-Uwe Bux Guest

    James Kanze wrote:

    > On 18 mai, 06:43, "Somebody" <> wrote:
    >> 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.


    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
     
    Kai-Uwe Bux, May 18, 2008
    #4
  5. Somebody

    Somebody Guest

    > 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.
     
    Somebody, May 18, 2008
    #5
  6. Somebody

    Somebody Guest

    > 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.
     
    Somebody, May 18, 2008
    #6
  7. Somebody

    Somebody Guest


    > 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
    :).
     
    Somebody, May 18, 2008
    #7
  8. Somebody wrote:

    >> 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.


    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.
     
    Paul Brettschneider, May 18, 2008
    #8
  9. Somebody

    James Kanze Guest

    On 18 mai, 20:02, "Somebody" <> wrote:
    > > 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.


    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.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, May 18, 2008
    #9
  10. Somebody

    James Kanze Guest

    On 18 mai, 20:05, "Somebody" <> wrote:
    > > 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?


    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.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, May 18, 2008
    #10
  11. Somebody

    Somebody Guest

    >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.
     
    Somebody, May 18, 2008
    #11
  12. Somebody

    Somebody Guest

    > 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.
     
    Somebody, May 18, 2008
    #12
  13. Somebody schrieb:
    >> 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.


    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.

    --
    Thomas
     
    Thomas J. Gritzan, May 18, 2008
    #13
  14. Somebody

    James Kanze Guest

    On May 18, 9:58 pm, "Somebody" <> wrote:
    > >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.


    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.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, May 19, 2008
    #14
  15. Hi!

    Somebody schrieb:
    > 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
     
    Frank Birbacher, May 20, 2008
    #15
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Koen
    Replies:
    4
    Views:
    404
    Sudsy
    Oct 27, 2004
  2. Kosio

    Floats to chars and chars to floats

    Kosio, Sep 16, 2005, in forum: C Programming
    Replies:
    44
    Views:
    1,344
    Tim Rentsch
    Sep 23, 2005
  3. Agix
    Replies:
    7
    Views:
    348
    dorayme
    May 10, 2007
  4. Jeremy
    Replies:
    6
    Views:
    378
    Jeremy
    Oct 26, 2009
  5. Ryan Macy

    Ideas needed & help needed!

    Ryan Macy, Jul 19, 2006, in forum: Ruby
    Replies:
    2
    Views:
    543
    Ryan Macy
    Jul 19, 2006
Loading...

Share This Page