A weird quirk about floats......

This is a discussion on A weird quirk about floats...... within the C++ Programming forums, part of the General Programming Boards category; I am working on an RPG and I use floating point variables for raw character stats. When they are displayed ...

  1. #1
    Registered User
    Join Date
    May 2007
    Posts
    27

    A weird quirk about floats......

    I am working on an RPG and I use floating point variables for raw character stats. When they are displayed (or used in combat) they are truncated to integers. I use the floating point variables to an accuracy of 0.1.

    I noticed that on a level up routine, if the number added to a particular stat (in this case, Agility), for instance, 1.2, would make the raw stat equal to a whole integer (e.x., 11.8 + 1.2 =13), the program would store it as 12.999999, and thus it would display improperly. On the next increase, the discrepancy would correct itself though......but the display would be wrong until then.

    Any good way of getting around this?

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,185
    If you know that everything is "supposed" to be in units of 0.1, I see one "quick fix" and one "real fix":
    The quick fix would be to, instead of starting at zero, start at 0.05. This is way bigger than the inherent error in floats (which is in the sixth significant digit, as I recall), but small enough to not give incorrect results.
    The real fix would be to use fixed point instead of floating point. (In other words, use an int variable to store "tenths of points", so instead of 11.8+1.2 not quite =13.0, you have 118+12=130.)

  3. #3
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Portugal
    Posts
    7,383
    It has to do with how floating point numbers are treated. You can read about it here, for instance: http://www.cygnus-software.com/paper...ringfloats.htm

    Meanwhile C++ doesn't have a round function. You can however simply add 0.5 to the end result of your sum and truncate the result to int
    The programmer’s wife tells him: “Run to the store and pick up a loaf of bread. If they have eggs, get a dozen.”
    The programmer comes home with 12 loaves of bread.


    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  4. #4
    pwns nooblars
    Join Date
    Oct 2005
    Location
    Portland, Or
    Posts
    1,094
    tabstop, The solution you propose as the real fix is the solution that a lot of shops use to curcumvent that issue because often you don't need more than one or two places.

    Heck if you really wanted to you could build an a class using int for the raw data but make it print out as a number with 2 decimal places, but that may be overkill for this application, and you would cut the max size of your numbers (but a int64 should be big enough for any realistic numbers in your game that would need decimals). In fact that gives me something to do, I think I am going to do that right now.

  5. #5
    Registered User
    Join Date
    Jun 2005
    Posts
    5,826
    Quote Originally Posted by Mario F. View Post
    Meanwhile C++ doesn't have a round function.
    Maybe not, but there are functions in <math.h> (or <cmath>) that round down (eg floor()), round up (eg ceil()). It is trivial to use these to create your own rounding functions (eg nearest integer, etc).

  6. #6
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,185
    Quote Originally Posted by grumpy View Post
    Maybe not, but there are functions in <math.h> (or <cmath>) that round down (eg floor()), round up (eg ceil()). It is trivial to use these to create your own rounding functions (eg nearest integer, etc).
    And also, C99 has introduced round, so perhaps your compiler makers have extended it to C++ (it appears that gcc/g++ has).

  7. #7
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,261
    If you want all of your floats to only be accurate to 0.1, then simply store them as the number of tenths in an int instead.
    I.e.
    1.6 becomes 16
    0.5 becomes 5
    To multiply say 1.6 by 0.5 you also divide by 10 (16 * 5) / 10 = 8 which means 0.8
    To divide say 1.6 by 0.5 you first multiply by 10: (10 * 16) / 5 = 32 which means 3.2
    Add and subtract works as normal.

    This is called fixed-point math. It's faster when the fixed-point scaling is a power of two, which is the more common case, and in any case it has perfect accuracy compared to using floats.
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  8. #8
    Registered User
    Join Date
    Jun 2005
    Posts
    5,826
    Quote Originally Posted by tabstop View Post
    And also, C99 has introduced round, so perhaps your compiler makers have extended it to C++ (it appears that gcc/g++ has).
    Relying on a C++ compiler supporting C99 features (or vice versa) is not a particularly good idea, unless you are willing to be locked into exactly one compiler, and possibly have your code break between versions of that compiler.

  9. #9
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,158
    Quote Originally Posted by grumpy View Post
    Relying on a C++ compiler supporting C99 features (or vice versa) is not a particularly good idea, unless you are willing to be locked into exactly one compiler, and possibly have your code break between versions of that compiler.
    Calling some function is hardly locking yourself in.

  10. #10
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Portugal
    Posts
    7,383
    It is when that function is a compiler extension, as is the case of the round function in some compilers cmath header.

    There are many ways to deal with this problem without incurring in a possible loss of portability. One such solution is:

    Code:
    int nearest_round(double val) {
        return int(val < 0 ? val - .5 : val +.5);
    }
    Last edited by Mario F.; 02-10-2008 at 12:56 PM. Reason: cast
    The programmer’s wife tells him: “Run to the store and pick up a loaf of bread. If they have eggs, get a dozen.”
    The programmer comes home with 12 loaves of bread.


    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  11. #11
    Registered User
    Join Date
    Jun 2005
    Posts
    5,826
    Quote Originally Posted by brewbuck View Post
    Calling some function is hardly locking yourself in.
    Rubbish. Calling a function that exists with one particular implementation is an effective way to lock yourself into that implementation -- unless, of course, you are writing trivial code that only you (as the lowest common denominator) will reuse. While such decisions are necessary sometimes, it is a bad decision if there are simple alternatives using portable approaches and a particularly bad decision in production environments in which code is reused, any changes require regression testing of affected applications, etc etc.

  12. #12
    Registered User
    Join Date
    Jan 2007
    Posts
    330
    Quote Originally Posted by Mario F. View Post
    It is when that function is a compiler extension, as is the case of the round function in some compilers cmath header.

    There are many ways to deal with this problem without incurring in a possible loss of portability. One such solution is:

    Code:
    int nearest_round(double val) {
        return int(val < 0 ? val - .5 : val +.5);
    }
    If you want want precision at 0.1 it should be 0.05 and you cant cast to int

  13. #13
    Registered User
    Join Date
    Jun 2005
    Posts
    5,826
    Quote Originally Posted by KIBO View Post
    If you want want precision at 0.1 it should be 0.05 and you cant cast to int
    As a matter of fact, it is an extremly poor idea to attempt to round a floating point value to the nearest multiple of 0.1 because such values cannot be represented by a floating point variable. This is a side effect of the fact that 1/10 (decimal, base 10) CANNOT be represented within a finite number of binary (base 2) digits.

    If you want data to be output as 0.1 rather than 0.0999991 simply print it to two decimal places. In other words, do the formatting of output rather than trying to tweak the value of the floating point variable.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. weird things with my linked list of queue
    By -EquinoX- in forum C Programming
    Replies: 3
    Last Post: 11-22-2008, 10:23 PM
  2. weird
    By kiz in forum C Programming
    Replies: 8
    Last Post: 09-24-2007, 01:16 AM
  3. Reading errors in floats
    By Improvolone in forum C++ Programming
    Replies: 8
    Last Post: 03-21-2006, 02:20 PM
  4. Getting weird characters in Strings
    By steve8820 in forum C Programming
    Replies: 3
    Last Post: 09-18-2001, 02:49 AM
  5. Using floats in conditional statements
    By DBB in forum C++ Programming
    Replies: 3
    Last Post: 09-12-2001, 07:54 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21