csharp.lang.correctness.double.double-epsilon-equality.correctness-double-epsilon-equality

profile photo of semgrepsemgrep
Author
unknown
Download Count*

Double.Epsilon is defined by .NET as the smallest value that can be added to or subtracted from a zero-value Double. It is unsuitable for equality comparisons of non-zero Double values. Furthermore, the value of Double.Epsilon is framework and processor architecture dependent. Wherever possible, developers should prefer the framework Equals() method over custom equality implementations.

Run Locally

Run in CI

Defintion

rules:
  - id: correctness-double-epsilon-equality
    patterns:
      - pattern: |
          $V1 - $V2
      - pattern-either:
          - pattern-inside: |
              ... <= Double.Epsilon
          - pattern-inside: |
              Double.Epsilon <= ...
      - pattern-not-inside: |
          double $V1 = 0;
          ...
      - pattern-not-inside: |
          double $V2 = 0;
          ...
      - pattern-not-inside: |
          $V1 = 0;
          ...
      - pattern-not-inside: |
          $V2 = 0;
          ...
    message: Double.Epsilon is defined by .NET as the smallest value that can be
      added to or subtracted from a zero-value Double. It is unsuitable for
      equality comparisons of non-zero Double values. Furthermore, the value of
      Double.Epsilon is framework and processor architecture dependent. Wherever
      possible, developers should prefer the framework Equals() method over
      custom equality implementations.
    languages:
      - csharp
    severity: WARNING
    metadata:
      references:
        - https://docs.microsoft.com/en-us/dotnet/api/system.double?view=net-6.0#testing-for-equality
        - https://docs.microsoft.com/en-us/dotnet/api/system.double.epsilon?view=net-6.0#platform-notes
      category: correctness
      technology:
        - .net
      confidence: MEDIUM
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]

Examples

double-epsilon-equality.cs

using System;

public class Example
{
   static bool IsApproximatelyEqual(double value1, double value2, double epsilon)
   {
      // If they are equal anyway, just return True.
      if (value1.Equals(value2))
         return true;

      // Handle NaN, Infinity.
      if (Double.IsInfinity(value1) | Double.IsNaN(value1))
         return value1.Equals(value2);
      else if (Double.IsInfinity(value2) | Double.IsNaN(value2))
         return value1.Equals(value2);

      // Handle zero to avoid division by zero
      double divisor = Math.Max(value1, value2);
      if (divisor.Equals(0))
         divisor = Math.Min(value1, value2);
      //ruleid: correctness-double-epsilon-equality
      return Math.Abs((value1 - value2) / divisor) <= Double.Epsilon;
   }

   static bool lazyEqualLeftCompare(double v1, double v2){
       //ruleid: correctness-double-epsilon-equality
       return Math.Abs(v1 - v2) <= Double.Epsilon;
   }

   static bool lazyEqualRightCompare(double v1, double v2){
       //ruleid: correctness-double-epsilon-equality
       return  Double.Epsilon <= Math.Abs(v1 - v2);
   }

   static bool uselessZeroEqual(){
       double v1 = 0;
       double v2 = 0;
       //ok
       return Math.Abs(v1 - v2) <= Double.Epsilon;
   }

   static bool isZero(double arg){
       double zero = 0;
       //ok
       return Math.Abs(arg - zero) <= Double.Epsilon;
   }

   static bool isZero2(double arg){
       double zero = 0;
       //ok
       return Math.Abs(zero - arg) <= Double.Epsilon;
   }

   static bool isZero3(double arg){
       double zero;
       zero = 0;
       //ok
       return Math.Abs(zero - arg) <= Double.Epsilon;
   }

   static bool isZero4(double arg){
       double zero;
       zero = 0;
       //ok
       return Math.Abs(arg - zero) <= Double.Epsilon;
   }
}