CodeQL queries 1.25
Skip to end of metadata
Go to start of metadata

Name: Possible loss of precision

Description: Dividing or multiplying integral expressions and converting the result to a floating-point value may result in a loss of precision.

ID: cs/loss-of-precision

Kind: problem

Severity: error

Precision: high

Query: PossibleLossOfPrecision.ql
/**
 * @name Possible loss of precision
 * @description Dividing or multiplying integral expressions and converting the result to a
 *              floating-point value may result in a loss of precision.
 * @kind problem
 * @problem.severity error
 * @precision high
 * @id cs/loss-of-precision
 * @tags reliability
 *       correctness
 *       external/cwe/cwe-190
 *       external/cwe/cwe-192
 *       external/cwe/cwe-197
 *       external/cwe/cwe-681
 */

import csharp

/** Holds if `e` is converted to type `t` which is a `float` or a `decimal`. */
predicate convertedToFloatOrDecimal(Expr e, Type t) {
  exists(CastExpr cast |
    cast.getExpr() = e and
    t = cast.getType()
  |
    t instanceof FloatingPointType or
    t instanceof DecimalType
  )
  or
  exists(BinaryArithmeticOperation op |
    op.getAnOperand() = e and
    convertedToFloatOrDecimal(op, t)
  |
    op instanceof AddExpr or
    op instanceof SubExpr or
    op instanceof MulExpr
  )
}

/** Holds if `div` is an exact integer division. */
predicate exactDivision(DivExpr div) {
  exists(int numerator, int denominator |
    numerator = div.getNumerator().stripCasts().getValue().toInt() and
    denominator = div.getDenominator().stripCasts().getValue().toInt() and
    numerator % denominator = 0
  )
}

/** An expression that may result in a loss of precision. */
abstract class LossOfPrecision extends Expr {
  Type convertedType;

  LossOfPrecision() {
    getType() instanceof IntegralType and
    convertedToFloatOrDecimal(this, convertedType)
  }

  /** Gets the alert message. */
  abstract string getMessage();
}

/** A division expression that may result in a loss of precision. */
class DivLossOfPrecision extends LossOfPrecision, DivExpr {
  DivLossOfPrecision() { not exactDivision(this) }

  override string getMessage() { result = "Possible loss of precision: any fraction will be lost." }
}

/** Holds if `e` is a constant multiplication that does not overflow. */
predicate small(MulExpr e) {
  exists(float lhs, float rhs, float res, IntegralType t |
    lhs = e.getLeftOperand().stripCasts().getValue().toFloat() and
    rhs = e.getRightOperand().stripCasts().getValue().toFloat() and
    lhs * rhs = res and
    t = e.getType() and
    not res < t.minValue() and
    not res > t.maxValue()
  )
}

/** A multiplication expression that may result in a loss of precision. */
class MulLossOfPrecision extends LossOfPrecision, MulExpr {
  MulLossOfPrecision() { not small(this) }

  override string getMessage() {
    result =
      "Possible overflow: result of integer multiplication cast to " +
        convertedType.toStringWithTypes() + "."
  }
}

from LossOfPrecision e
select e, e.getMessage()

Converting the result of dividing, or multiplying, two integral expressions to a floating-point value may result in a loss of precision. For integral division, any fractional component of the result will be lost. For integral multiplication, the result may be outside the integral range and overflow.

Recommendation

For division, unless the intent is to round the result down to a whole number, you should cast one of the operands to a floating-point type before performing the division. For multiplication, unless the intent is to overflow, you should cast one of the operands to a floating-point type before performing the multiplication.

Example

In this example c is equal to 5 because integer division is performed.

void DivisionLossOfPrecision()
{
    int a = 21;
    int b = 4;
    float c = a / b;
}

Casting one of the integers to a float ensures that float division is used and the remainder will be maintained, giving c the value of 5.25.

void DivisionNoLossOfPrecision()
{
    int a = 21;
    int b = 4;
    float c = (float)a / b;
}

In this example, if a is greater than 536,870,911 the result will overflow.

void MultiplicationLossOfPrecision(int a)
{
    int b = 4;
    float c = a * b;
}

Casting one of the integers to a float ensures that float multiplication is used and overflow is avoided.

void MultiplicationNoLossOfPrecision(int a)
{
    int b = 4;
    float c = (float)a * b;
}

References
  • J. Albahari and B. Albahari, C# 4.0 in a Nutshell - The Definitive Reference, p.24.
  • MSDN, C# Reference / operator, * operator.
  • Common Weakness Enumeration: CWE-190.
  • Common Weakness Enumeration: CWE-192.
  • Common Weakness Enumeration: CWE-197.
  • Common Weakness Enumeration: CWE-681.