comparisons to floating point variables
-
- Developer
- Posts: 2150
- Joined: Mon Apr 23, 2007 1:17 am
- Location: Pennsylvania
- Contact:
comparisons to floating point variables
This deals with double and float types.
In many places we do comparisons with floats/doubles directly. This is a problem because there is always some amount of rounding error associated with these datatypes, that's why we have an epsilon or required precision macro (or should have one).
for instance in unit_collide.cpp line around 440
>if (c<0||a==0)
c and a are doubles. The problem is the a == 0. I dont believe we can ever guarantee that a doesn't equal 0.0000001 when it should be 0 on some machines. We should send any direct comparisons through a macro that basically has a set precision value and returns if we match within that precision or not.
That's a pretty standard way to compare doubles and floats, yet we dont seem to do it.
In many places we do comparisons with floats/doubles directly. This is a problem because there is always some amount of rounding error associated with these datatypes, that's why we have an epsilon or required precision macro (or should have one).
for instance in unit_collide.cpp line around 440
>if (c<0||a==0)
c and a are doubles. The problem is the a == 0. I dont believe we can ever guarantee that a doesn't equal 0.0000001 when it should be 0 on some machines. We should send any direct comparisons through a macro that basically has a set precision value and returns if we match within that precision or not.
That's a pretty standard way to compare doubles and floats, yet we dont seem to do it.
Ed Sweetman endorses this message.
-
- Lead Network Developer
- Posts: 2560
- Joined: Sun Jan 12, 2003 9:13 am
- Location: Palo Alto CA
- Contact:
-
- Developer
- Posts: 2150
- Joined: Mon Apr 23, 2007 1:17 am
- Location: Pennsylvania
- Contact:
perhaps, but anytime you try to see if a double or float is equal to a number, you have to work in your accepted precision, because anything except very specific numbers gets rounded with some degree of error. So checking for 0 to indicate something with a float after some operation, wont necessarily work all the time.
I'm just saying, i haven't seen any type of floating point macro that handles such a comparison and we do make them in other places too.
I'm just saying, i haven't seen any type of floating point macro that handles such a comparison and we do make them in other places too.
Ed Sweetman endorses this message.
-
- Elite
- Posts: 7243
- Joined: Mon Apr 18, 2005 2:40 pm
- Location: LS87, Buenos Aires, República Argentina
Safemode: don't ever try macros. They would bloat generated code and make it (even) slower. Rather, if you want to compare floats against doubles, make sure you compare using target precision.
Ie: if you're going to divide by b like this:
You want to make sure that ((float)b) != 0.
That's quite faster and safer and... more readable.
Ie: if you're going to divide by b like this:
Code: Select all
float a;
double b;
float x=a/b;
That's quite faster and safer and... more readable.
-
- Developer
- Posts: 2150
- Joined: Mon Apr 23, 2007 1:17 am
- Location: Pennsylvania
- Contact:
-
- Elite
- Posts: 7243
- Joined: Mon Apr 18, 2005 2:40 pm
- Location: LS87, Buenos Aires, República Argentina
Why wouldn't (float)a == (float)b work?
If you really need a specific precision that's not float's epsilon (FLT_EPSILON), then I guess "fabsf(a-b) < precision" would work, but just in that very specific case.
Anyway, if you want macros, try:
BTW: I never tried parameterizing operators... does it work?
If you really need a specific precision that's not float's epsilon (FLT_EPSILON), then I guess "fabsf(a-b) < precision" would work, but just in that very specific case.
Anyway, if you want macros, try:
Code: Select all
#define FCMP(a,b,op) ((float)(a) op (float)(b))
#define FEQ(a,b,precision) (fabsf((a)-(b)) < (precision))
#define FEQ(a,b) FEQ(a,b,==)
#define FNZERO(a) FCMP((a),0,!=)
-
- Insys Pilot
- Posts: 2
- Joined: Tue Jul 10, 2007 5:10 pm
can't you write it like:safemode wrote:that works for literals, but what about instances where we compare against another float/double?
we'd need a function that we can say a > target - precision && a < target + precision
abs(a-target) < epsilon
and have epsilon a constant (FLT_EPSILON)
Edit: right that's in the macro's in the previous post. But is too hard to just use that directly instead of macro's ?
-
- Minister of Information
- Posts: 1895
- Joined: Fri Jan 31, 2003 9:40 pm
- Location: The land of tenure (and diaper changes)
To be more precise, basing a decision upon direct equality testing between two floats/doubles isn't really the issue. The problem is basing a decision upon direct equality testing of the results of two different sets of operations upon floats/doubles without using a precision bound. Comparing non-mutable quantities will alway work just fine, so constants and externally defined values aren't too worrisome, but order-of-operations influences rounding decisions, so values generated upon two different paths may not be exactly the same, even in systems that perform rounding in a fashion actually compliant with the IEEE754 floating point specifications.
However, I would be very surprised if there were frequent places in the code where such equality checks were occurring, excepting checks against 0 (which are probably all fine, because the whole point of those tends to be to catch the exceptional cases where the value actually is 0 and then do something different).
As for greater-than and less-than comparisons, given the underlying looseness and non-mission-critical (in the traditional "lives depend on your software" sense) nature of the modeling we're doing (our time slices are fairly large), I am doubtful that any measurable impact on object level simulation accuracy would be gained from adding operations to check against fudge factors.
However, I would be very surprised if there were frequent places in the code where such equality checks were occurring, excepting checks against 0 (which are probably all fine, because the whole point of those tends to be to catch the exceptional cases where the value actually is 0 and then do something different).
As for greater-than and less-than comparisons, given the underlying looseness and non-mission-critical (in the traditional "lives depend on your software" sense) nature of the modeling we're doing (our time slices are fairly large), I am doubtful that any measurable impact on object level simulation accuracy would be gained from adding operations to check against fudge factors.
-
- Developer
- Posts: 2150
- Joined: Mon Apr 23, 2007 1:17 am
- Location: Pennsylvania
- Contact:
while debugging VS for floating and double inconsistencies, i've come across many places where we use QVector's, which are internally double, with floats. Both taking the values of QVector's data members, or setting them.
I'm not sure how that affects gameplay, but anything that i come across that takes a value from QVector, i'm making a double. I assume if you're reading a double, you are storing to a double. Otherwise you would use regular Vector's, not QVector's.
btw, our Vector class is retarded. Why wouldn't we just make it a templated Vector class? That makes magnitudes more sense than the preprocessor garbage we currently do with it.
I'm not sure how that affects gameplay, but anything that i come across that takes a value from QVector, i'm making a double. I assume if you're reading a double, you are storing to a double. Otherwise you would use regular Vector's, not QVector's.
btw, our Vector class is retarded. Why wouldn't we just make it a templated Vector class? That makes magnitudes more sense than the preprocessor garbage we currently do with it.
Ed Sweetman endorses this message.
-
- Elite
- Posts: 8014
- Joined: Fri Sep 05, 2003 4:03 am
- Location: Montreal
- Contact:
Even if doing so were 100% safe, floating type equality comparisons are highly deprecated, and for good reasons. First of all is the principle of code being self-documenting.jackS wrote:... excepting checks against 0 (which are probably all fine, because the whole point of those tends to be to catch the exceptional cases where the value actually is 0 and then do something different).
There was a time when the fashion was to write copious amounts of comments. But then some gurus began to cast doubt on comments: They argued, quite rightly, that comments tend to lose syncronization with the code which the comment on, as the code is updated; --since keeping comments up to date relies on programmer discipline. So, they advocated, instead, to make sure that, as much as possible, the intentions in code be made obvious from the code itself. And this is today's wisdom.
Equality comparisons between floats is one way to trample this self-documentation aspect. A programmer looking at "if( foo == bar ) ..." has a right --and even a responsibility-- to assume that a and b are anything BUT floating point types. Specially when, as I've seen in many cases in vs code, the rhs of the comparison doesn't even look like an fp, such as,
Code: Select all
if( fuel == 0 ) ...
So, a major improvement would be to change such statements to read
Code: Select all
if( fuel == 0.0f ) ...
Code: Select all
if( fuel == 0.0d ) ...
But even so, the obvious meaning of such a conditional would be "if the gas tank is empty ..."; but what I've seen in vs code is that often such obvious meanings are NOT the real meanings; but, say, it might mean "if this type of unit doesn't use fuel becaus it's not self-propelled ...".
This is a totally imaginary example because I don't remember what the actual instances were, but I do remember encountering unintuitive true/false conditions being encoded as "special values" of fp variables.
One good book, Code Complete --I forget the author's name now-- dedicate a whole sub-chapter to advocating the erradication of this malpractice of encoding special conditions as special values of variables; --and any variables, not just fp ones; as doing so makes code many times harder to understand.
When a variable must be accompained by special conditions, such as fuel not being warranted, the right thing to do is create a struct, like
Code: Select all
struct fuel
{
bool is_warranted;
float litres;
};
Latest version of Cinemut Opaque
Latest version of LaGrande noodleworks (scroll down).
An evolving La Grande How-To...
The non-working, but latest, CineMut test_bike
PU (Privateer: Parallel Universe's Home). WC or Privateer Drayman for you?
WCpedia --The Wing Commander Encyclopedia-- From Angel Deveraux through Belisarius to Zachary Banfeld...
WC Nexus forum, the Moonbase Tycho of WC fans.
Latest version of LaGrande noodleworks (scroll down).
An evolving La Grande How-To...
The non-working, but latest, CineMut test_bike
PU (Privateer: Parallel Universe's Home). WC or Privateer Drayman for you?
WCpedia --The Wing Commander Encyclopedia-- From Angel Deveraux through Belisarius to Zachary Banfeld...
WC Nexus forum, the Moonbase Tycho of WC fans.