0

I want to compare a number to its reference value, up to the precision of the reference.

If my reference has 2 decimals, I want to round the number to two decimals and then compare it with the reference.

If my reference is 7.95 :

7.942 is not OK
7.949 is OK
7.952 is OK
7.956 is not

I made my own function below, which works, but there maybe is something already available.

    public bool IsEqual(double value, double reference)
    {
        var sReference = reference.ToString(System.Globalization.CultureInfo.InvariantCulture);
        var decimals = sReference.Length - sReference.IndexOf('.') - 1;
        return Math.Abs(reference - value) < Math.Pow(10, -decimals);
    }

EDIT: I edited the question to make it more explicit.

  • I don't need to set an epsilon, as it is set by the reference value precision. So all your kind suggestions of other topics that need to specify an epsilon do not apply to my need.
  • I agree that using string conversion is not satisfying. Namely, large numbers represented in scientific notation don't work in my exemple. It's not my main focus for now, as i'm dealing with values like #.00 or #.000
geriwald
  • 190
  • 1
  • 4
  • 17
  • 1
    Does Math.Round give you want you need? – Russ Jan 08 '21 at 15:38
  • 1
    Different people will have different requirements for *how* equal two quantities have to be to be considered equal (possibly both in terms of absolute as well as relative differences). – Damien_The_Unbeliever Jan 08 '21 at 15:39
  • OK, i will explain my question differently. – geriwald Jan 08 '21 at 15:40
  • Does this answer your question? [C# Compare two double with .Equals()](https://stackoverflow.com/questions/40653375/c-sharp-compare-two-double-with-equals) – Mark Benningfield Jan 08 '21 at 15:40
  • 2
    Doing any comparison of floating-point numbers through string conversion is almost inherently suspect, as there are many values that can trip such things up. Doubly so if you use no format string. Try your method on `1e20`, for example. – Jeroen Mostert Jan 08 '21 at 15:40
  • 1
    https://stackoverflow.com/a/2411661/17034 – Hans Passant Jan 08 '21 at 15:40
  • I made my question more explicit, I hope. – geriwald Jan 08 '21 at 15:46
  • One problem is, doubles don't "have" decimals. E.g. imagine trying to use your method to check that something is equal to `2` with two decimal places. `2.0`, `2.00` and `2.000` all produce exactly the same `double` value. – Damien_The_Unbeliever Jan 08 '21 at 15:48
  • @Damien_The_Unbeliever I'm converting doubles to string, not the other way round, so I should be good. – geriwald Jan 08 '21 at 15:55
  • No, I mean no matter whether you call this method with `IsEqual(...,2.0)`, `IsEqual(...,2.00)` or `IsEqual(...,2.000)`, your method will always calculate `decimals` as the same value - do you see why this is problematic as a means to communicate to the method how many decimals should be checked? – Damien_The_Unbeliever Jan 08 '21 at 15:57
  • MarkBenningfield Russ HansPassant : I don't want to specify an epsilon, the precision is that of my reference value – geriwald Jan 08 '21 at 15:57
  • 1
    There is no `double` that actually has the value `7.95`, as that value cannot be exactly represented. The actual value will be something like `7.9500000000000001776356839400250464678` -- but presumably that's not what you want to use for your "reference". Unlike `decimal`s, floating-point values do not store precision, and it is very perilous to rely on the number of decimals you get from string formatting, as even a seemingly simple calculation can screw that up -- try `7.95 - 0.031`. – Jeroen Mostert Jan 08 '21 at 15:58
  • @Damien_The_Unbeliever oh yeah you're right. I'm dealing with irrational values so i didn't think about this possibility. – geriwald Jan 08 '21 at 16:00
  • @JeroenMostert MMhhh i'm beginning to reconsider my life's choices. – geriwald Jan 08 '21 at 16:03
  • I don't understand why my question is marked as a duplicate. I know they are close, and i read the other question before asking mine, because the problem is not identical. I agree that my title is not very explicit, but the question IS NOT a duplicate. – geriwald Jan 08 '21 at 16:07
  • What you likely want to do is round things to the appropriate number of decimal points (which, as everyone has pointed out, will likely give you an approximate value). Then, calculate an epsilon based on that number of decimal points you care about and then do a standard floating point comparison using that epsilon. I used to do this all the time back in my control systems days (each measurement point included information about "the precision that made sense for this value") – Flydog57 Jan 08 '21 at 16:13
  • @Flydog57 Yeah, my epsilon is Math.Pow(10, -decimals) – geriwald Jan 08 '21 at 16:14
  • There is no problem if you can store or supply `decimals` in that calculation *separately*. There is a problem if you want to derive it from existing values, however, which I suggest you don't do -- or switch to `decimal` if you have to. – Jeroen Mostert Jan 08 '21 at 16:20
  • This is what I came up with `public static bool IsEqual(double value, double reference, int precision){var newReference = Math.Round(reference, precision, MidpointRounding.AwayFromZero); var epsilon = Math.Pow(10.0, -(precision + 1)) * 5.0; return Math.Abs(newReference - value) < epsilon; }`. It seems to work with your examples (I tested your 4 examples with this: `IsEqual(theValue, reference, 2)`) – Flydog57 Jan 08 '21 at 16:25
  • @GéraudBenazet: Note that my epsilon is half of what you say. I showed it as `Math.Pow(10.0, -(precision + 1)) * 5.0`, but it could easily be `Math.Pow(10.0, -precision) / 2.0`. That might have been your issue. The debugger is a big help here :-) – Flydog57 Jan 08 '21 at 17:55
  • @Flydog57 sorry i was away. Thanks for your answers and propositions, but as stated in my question, my function works. I was just wondering why it wasn't a standard function. I think i understand why now, i didn't think about the trailing zeros. – geriwald Jan 21 '21 at 16:47

0 Answers0