2

Is it safe to compare C# doubles or floats against assigned, default values?

For example:

public class Foo
{
    private double defaultEfficiency = 100.0;
    public double efficiencyBar = defaultEfficiency;

    public bool IsBarAtDefaultValue()
    {
        if(efficiencyBar == defaultEfficiency)
            return true;
        else
            return false;
    }
}

So my question is if the check within IsBarAtDefaultValue() is going to work as I expect it to? ie. It will return true if efficiencyBar is the same as defaultEfficiency.

This SO question: Is it safe to check floating ... asks about a specific case when the default value is 0.0. My question concerns the broader case of any default value.


To provide a more concrete example...

I am working on an application that deals with motor efficiencies which are generally in the range of 0 - 100%, or 0.0 to 1.0. The user has the ability to define new motors and assign various efficiencies.

In the panel to define a new motor, I want to populate the various efficiencies with default values. Later on, I want to check and see if the user altered the value to something other than the default. For example, I want to check and see if they made changes but accidentally forgot to save their work.

That led to my wondering about what value is actually used in the assignment of a default value for floating point types (double & float). The linked SO question discusses the 0.0 case, but I wondered about the broader (not 0.0) case as I don't want to use 0.0 for the default values for the efficiencies.

Community
  • 1
  • 1
  • When dealing with `floats` and `doubles` in the general case, I believe you are supposed to use an epsilon (from my numerical computation class). However, your specific case of `d = 100.0` might be OK. – jww May 01 '14 at 15:47
  • 1
    I don't really understand the question, so instead of answering it, I'll just say that `IsBarAtDefaultValue` can be rewritten as `return efficiencyBar == defaultEfficiency` – Ben Aaronson May 01 '14 at 15:47
  • 1
    If you're dealing with a slider or other "bar" control in GUI, and need to do comparison like that I'd recommend using a range of integer values for the bar, and then converting to the double value only when you need it. Then the default would be an integer, and much easier to deal with. – hyde May 01 '14 at 16:21
  • @BenAaronson - Agreed. I used the expanded version in the example to make my intent more clear. Hopefully my edit my intent more clear. I'm primarily concerned with detecting if a floating point type has been changed from its default value. –  May 01 '14 at 17:01
  • @GlenH7 Ah okay. Well I don't know well enough to say for sure but I believe the answer is yes. Still I think other answers like a custom setter are probably better design-wise – Ben Aaronson May 01 '14 at 17:28
  • You: `public double efficiencyBar = defaultEfficiency;` But a field initializer cannot refer to a non-static member of the same instance, here `defaultEfficiency`. The code you show should not be legal. – Jeppe Stig Nielsen May 05 '14 at 19:27

5 Answers5

2

Yes it is safe to do this. Consider that your code is just doing this:

double x = ...;
double y = x;
bool t = (x == y); //always true regardless of the value of x
Timothy Shields
  • 75,459
  • 18
  • 120
  • 173
2

Well, yes and no. If all of the floating-point values you are comparing are stored as constants and the constants are the same data width (i.e., you compare float to float and double to double), this is a safe operation. But if everything you were comparing was a constant, why not use integers or enums?

In general, comparing floating-point numbers for equality is unsafe. The reason for this is that floating-point numbers can't perfectly store all values. This is a lot like the problem with the number 1/3 in decimal which we would have to write as 0.33333... The same problem exists for storing fractional parts in binary, and numbers that have finite representations in decimal notation are not guaranteed to have finite binary representations. Since we are limited to 32 or 64 bits, part of the number gets truncated. This means that performing math operations on floating-point numbers could result in unexpected consequences.

Consider this quote from this post from Bruce M. Bush:

At the heart of many strange results is one fundamental: floating-point on computers is usually base 2, whereas the external representation is base 10. We expect that 1/3 will not be exactly representable, but it seems intuitive that .01 would be. Not so! .01 in IEEE single-precision format is exactly 10737418/1073741824 or approximately 0.009999999776482582.

You should usually check equality in floating-point values using some small epsilon for variance.

public class Foo
{
    //Choose a small value that is appropriate for your needs
    //see the info below for some info from Microsoft
    private static double epsilon = 0.00001;
    private double defaultEfficiency = 100.0;
    public double efficiencyBar = defaultEfficiency;

    public bool IsBarAtDefaultValue()
    {
        //we use the absolute value of the difference. If this is smaller than
        //epsilon, then the value is "good enough" for equal
        if (Math.Abs(efficiencyBar - defaultEfficiency) < epsilon)
            return true;
        else
            return false;
    }
}

You could use something like Double.Epsilon for your epsilon value, but this is probably way too small for your needs and is recommended against in the documentation:

If you create a custom algorithm that determines whether two floating-point numbers can be considered equal, we do not recommend that you base your algorithm on the value of the Epsilon constant to establish the acceptable absolute margin of difference for the two values to be considered equal. (Typically, that margin of difference is many times greater than Epsilon.)

And in their documentation on the Double.Equals() method:

Because Epsilon defines the minimum expression of a positive value whose range is near zero, the margin of difference between two similar values must be greater than Epsilon. Typically, it is many times greater than Epsilon. Because of this, we recommend that you do not use Epsilon when comparing Double values for equality.

Both places are good sources of additional information on comparing floating-point numbers safely.

Sam
  • 1,176
  • 6
  • 19
  • 1
    Can you convert your second sentence into an actual sentence by adding a verb? – Kirk Woll May 01 '14 at 15:49
  • Does using `Min` and 2x `Abs` like that really make any useful difference? I mean something like, is that documented somewhere and explained why that is a good pattern, in more detail? – hyde May 01 '14 at 16:02
  • @hyde Good point, I was trying to account for an issue that doesn't exist! Removed the extra complexity – Sam May 01 '14 at 16:06
  • 2
    It's worth noting that a method like this should NOT be used inside an `IEqualityComparer` or `IEquatable` implementation because it breaks the transitivity of equality. – Ben Aaronson May 01 '14 at 16:16
  • Thank you for the answer, and I have updated my question to explain _why_ I'm asking about if it's safe or not. Enums or ints won't work because I need to use real numbers that will vary. And I may have motors with a 42.314% efficiency so I need to use floating point. My primary interest is in detecting when a variable has been changed from its default value. –  May 01 '14 at 17:04
  • @GlenH7 If all you want to do is detect a change, you might want to consider storing a `boolean` value in your class and modifying it in a setter method when the value gets changed from the default. That would allow detecting even if the user changed it to the default value again, assuming you cared about that case. You could even go as far as to define an event that you could listen for in a separate thread that got raised when you changed the value. – Sam May 01 '14 at 17:09
  • @Sam - that was actually my initial route. But it got complicated due to the UI technology I'm using, my lack of expertise in the UI technology, and the objects / lists I'm interacting with. Those stumblings led me to asking this question. :-) –  May 01 '14 at 17:15
  • @GlenH7 Then it might actually be OK for you to use `==` to compare, unless you want to detect the case where the value ends up back at the default through arithmetic manipulations. I have some faint bells ringing about [Dependency Properties](http://msdn.microsoft.com/en-us/library/ms752914.aspx) (also a good [YouTube tutorial](https://www.youtube.com/watch?v=XYCHO9K34qA)) in UIs, but I think they might only be applicable in WPF applications. – Sam May 01 '14 at 17:39
0

Because efficiencyBar is public and can be modified outside of the class, you have no idea what it could be set to and if floating point prevision issues come into play dependent on the specified new values at run-time. It would be best then to compare against an epsilon value, due to floating point precision issues.

return (Math.Abs(efficiencyBar - defaultEfficiency) < Double.Epsilon);
pinkfloydx33
  • 11,863
  • 3
  • 46
  • 63
  • I'm pretty sure that this is exactly the same as `efficiencyBar == defaultEfficiency`. [Double.Epsilon is not the epsilon you think it is](http://www.codeproject.com/Articles/86805/An-introduction-to-numerical-programming-in-C), and if we had access to the "real" epsilon, you'd want to scale it by 100 (since the numbers here are percentages) and further multiply with a factor to express the allowed imprecision (for which 1 is useless if you use `<` rather than `<=`). – Stein May 22 '15 at 20:24
0

If the primary goal is to detect if the value changed from a default/initial value, you could just define a custom setter that kept track of whether the value was ever changed.

public class Foo
{
    private double defaultEfficiency = 100.0;
    private double _efficiencyBar = defaultEfficiency;

    public double efficiencyBar
    {
        get
        {
            return _efficiencyBar;
        }
        set
        {
            _efficiencyBar = value;
            _efficiencyBarChanged = true;
        }
    }

    private bool _efficiencyBarChanged = false;
    //Now you know if it was ever changed, period
    //even if it got changed back to the default value
    public bool IsBarAtDefaultValue
    {
        get
        {
            return !_efficiencyBarChanged;
            //if you preferred, this could still be an equality-like test
            //but keeping track of a state change in a bool value makes more sense to me
        }
    }
}

If you wanted to be able to "reset" the value to default and have your check come back false, I would recommend a method like the following:

public void resetEfficiencyBar()
{
    _efficiencyBar = defaultEfficiency;
    _efficiencyBarChanged = false;
}

This avoids all of the complications associated with floating-point comparisons, and I think makes the intent of your code more clear.

Sam
  • 1,176
  • 6
  • 19
  • Thank you for taking the time to put this answer together. As I had mentioned, it gets complicated at this point. Foo is a ViewModel and the efficiencies are Properties bound to textboxes within the View. The user has the ability to select through existing motors which updates the value of the efficiency Properties. This approach will create false positives on change detection which is what I'm trying to avoid. That's why I switched my focus to looking at the doubles and assigning default values. –  May 01 '14 at 17:29
0

Note that all integers of magnitude less than or equal to 253+1 are exactly representable in IEEE754 double precision and consequently your IsBarAtDefaultValue function will do exactly what you expect it to in your case of defaultEfficiency equal to 100.0.

thus spake a.k.
  • 1,607
  • 12
  • 12