4 ms·
In general, this is good advice. However, there are cases where comparing floating point numbers for equality works just fine. For example, if you have a varia
by spacechild1 3y ago
In general, this is good advice.
However, there are cases where comparing floating point numbers for equality works just fine. For example, if you have a variable 'v' and want to update it to a new value 'f', you can do 'if (v == f)' to check if the variable would change. Or if you have a sentinel value -1.0, it is perfectly ok to do 'if (a == -1.0)'
In general, if you look for a number 'f' in variable 'v', you can safely use equality as along as you expect 'v' to be set to 'f', i.e. the value of 'v' is not the result of a computation. This may sound trivial, but this is often omitted when discussing floating point comparison. If someone knows a scenario where the examples above would break, I would be curious to hear! (My main field is audio programming and code like this is omnipresent.)
- stouset 3y ago> For example, if you have a variable 'v' and want to update it to a new value 'f', you can do 'if (v == f)' to check if the variable would change. Isn't it strictly faster to simply assign? No branching, no branch predictions, only one instruction in all cases.
- kstenerud 3y agoIt depends on whether you need a set operation or a test-and-set operation.
- karpierz 3y agoAssuming that all you want to do is assign, then sure. If you want to log the change or do any additional conditional logic, then that wouldn't work.
- spacechild1 3y agoA typically pattern is: float freq = getParameter(FILTER_FREQUENCY); float coeff; if (freq != mFreq) { coeff = calculateCoeffFromFrequency(freq); // expensive mFreq = freq; mCoeff = coeff; } else { coeff = mCoeff; // use cached value } // use coeff
- bee_rider 3y agoCould you do some threshold-based comparison? What if freq is changed only a little tiny bit?
- spacechild1 3y agoI can certainly imagine cases where this would make sense.
- munk-a 3y agoI know there is a fair bit of division on this point but I've always liked type mixing, in languages where this is available, to handle signal values and preferred separate variables where it is not. I'd prefer to `if (v IS NULL)` a nullable float compared to checking a constant value - or else to use a guard value to test the validity i.e. `if x = FREQUENCY_SET // enter logic involving the frequency stored in v` - that approach is even better if you can use tuples/structs to glue the status and value fields together. I am a quickly crustifying developer who was an enthusiatic value packer in their younger years but I've changed my tune to be less afraid of using more variables if they are clearer - even if that comes at a cost to data efficiency (and like, an extremely negligible cost in most situations). Though I don't have any objections to value packing for serialization - that's a fundamentally different domain.
- kragen 3y agocesaref's comment is not good advice, it's not advice at all, it's a snarky take on how floating point is trickier than it looks almost invariably floating-point calculations end up in some kind of comparison; if they didn't we'd probably do the calculations in a galois field or something instead even equality comparison of floats is useful; there are an abundance of seminumerical algorithms which use it correctly on the results of computations. a trivial example is computing an attractive fixpoint by iterating a function and comparing each hare result to the previous iteration and to a tortoise. you have to be especially careful about x87 extended precision here ieee-754 requires bit-exact results for the five fundamental operations here is an example where your 'safe' generalization is not safe: #include <stdio.h> int main() { float f = 0.7; printf(f == 0.7 ? "ok\n" : "wtf\n"); return 0; } this prints 'wtf' on, for example, x86_64-linux-gnu
- shultays 3y agowell you shouldn't expect much if you are comparing floats to doubles
- spacechild1 3y agoTrue, but it's an easy mistake to make.
- tialaramex 3y agoIt's an easy mistake to make in C (and C++) because of their lackadaisical approach to type safety. In Rust if we insist we want to compare an f32 (what C would call float) to an f64 (double), the language says it doesn't have a way to do that, highlighting our mistake. If we just try to compare an f32 with value 0.7 against 0.7, the type inference concludes 0.7 is also an f32 so it's equal, and if we do the same with an f64, this time by inference 0.7 is an f64 and so again it's equal.
- spacechild1 3y agoRust's behavior makes so much more sense! IMO, implicit conversions is one of C++ biggest weaknesses. Yes, it may save some keystrokes, but it can easily lead to subtle (or not so subtle) bugs.