Back to Blog
C#

C# Modulo Operator: Syntax and Common Pitfalls

c# modulo operator: Understand how the C# modulo operator works with integers and floating-point numbers, including negative operands, common pitfalls, and practical u...

C#modulointeger arithmeticfloating-pointnegative numbers
Illustration of the C# modulo operator showing division and remainder on a number line.

The C# modulo operator (%) returns the remainder after integer division. It is a binary operator that works on numeric types, but its behavior with negative operands and floating-point values often surprises developers. This article explains the syntax, runtime behavior, and practical applications of the operator, with attention to edge cases that matter in production code.

How the Modulo Operator Works in C#

For integer operands, a % b computes the remainder after dividing a by b. The result satisfies the relationship a = (a / b) * b + a % b, where a / b is integer division that truncates toward zero. This means the sign of the result is determined by the sign of the dividend (the left operand), not the divisor.

int a = 10; int b = 3; int remainder = a % b; // 1

The operator is defined for all built-in numeric types: int, long, short, byte, float, double, and decimal. For integral types, both operands must be integers; mixing int and long is allowed because of implicit conversion. For floating-point types, the result is the remainder of a floating-point division, which can be a non-integer value.

Modulo with Negative Numbers

One of the most common misunderstandings is how % handles negative operands. In C#, the result takes the sign of the dividend. For example:

int result1 = -10 % 3; // -1 int result2 = 10 % -3; // 1 int result3 = -10 % -3; // -1

This behavior differs from languages like Python, where the result takes the sign of the divisor. If you need a mathematically consistent modulo (always non-negative), you must adjust the result manually:

int Mod(int a, int b) { int r = a % b; return r < 0 ? r + b : r; }

This is important when using modulo for indexing or cyclic logic where negative indices are not acceptable.

Modulo with Floating-Point Types

The % operator also works with float, double, and decimal. For floating-point operands, the result is the remainder of a floating-point division, which can be a non-integer value. For example:

double a = 5.5; double b = 2.0; double result = a % b; // 1.5

The IEEE 754 remainder operation is used for float and double. This is not the same as the mathematical modulo for negative numbers; the sign rule is the same as for integers: the result takes the sign of the dividend. Precision issues can arise because floating-point arithmetic is not exact. For example, 0.1 % 0.02 may not produce exactly 0.0 due to binary representation.

For decimal, the operator behaves similarly but with decimal precision, which avoids binary rounding errors. However, decimal is significantly slower than double for arithmetic operations, so choose based on your precision and performance requirements.

Common Use Cases for the Modulo Operator

The modulo operator is used in many everyday coding patterns:

  • Checking even or odd: number % 2 == 0 is a classic test.
  • Cycling through array indices: index % array.Length wraps an index within bounds.
  • Converting seconds to minutes/hours: totalSeconds % 60 gives the remaining seconds.
  • Limiting a value to a range: value % range ensures the result stays within [0, range-1] for positive values.
int[] items = { 1, 2, 3, 4 }; for (int i = 0; i < 10; i++) { int item = items[i % items.Length]; Console.WriteLine(item); }

When using modulo for indexing, always be aware of negative inputs. If the index can be negative, the result may be negative, causing an IndexOutOfRangeException. In such cases, use the non-negative adjustment shown earlier.

Modulo vs. Math.DivRem

When you need both the quotient and the remainder, Math.DivRem can be more efficient than using division and modulo separately. Math.DivRem has overloads for int and long that compute both results in one operation, potentially avoiding a redundant CPU instruction.

int quotient; int remainder = Math.DivRem(10, 3, out quotient); // quotient = 3, remainder = 1

The out parameter receives the quotient. This method is available in .NET Core 2.0+ and .NET Standard 2.1. For older frameworks, you can use the older Math.DivRem(int, int, out int) which exists since .NET Framework 2.0. If you only need the remainder, the % operator is simpler and just as fast.

Performance and Precision Considerations

For integer types, the % operator compiles to a single CPU instruction on most platforms, so it is extremely fast. For floating-point types, the operation is more expensive because it involves floating-point division and remainder calculations. If you are doing many modulo operations in a tight loop, consider whether you can use integer arithmetic instead.

Precision is a concern with float and double. Because these types are binary fractions, values like 0.1 are not represented exactly. This can lead to surprising results:

double x = 0.1; double y = 0.02; double r = x % y; // Not exactly 0.0

If you need exact decimal arithmetic, use decimal. However, decimal operations are significantly slower than double. For financial calculations, decimal is the right choice; for scientific computations where small errors are acceptable, double is usually fine.

Another edge case is overflow. For integer types, % can throw an OverflowException if the dividend is int.MinValue and the divisor is -1. This is because int.MinValue / -1 would overflow to int.MaxValue + 1, which is not representable. The same applies to long.MinValue % -1. Always guard against this when the divisor can be -1.

Edge Cases and Pitfalls

Division by zero is the most obvious pitfall. For integer types, a % 0 throws a DivideByZeroException. For floating-point types, a % 0 returns NaN (Not a Number) for float and double, but throws for decimal. This difference is important when writing generic numeric code.

int i = 10 % 0; // DivideByZeroException double d = 10.0 % 0.0; // NaN decimal m = 10m % 0m; // DivideByZeroException

Negative divisors are legal but often confusing. As shown earlier, the sign of the result is always the sign of the dividend, regardless of the divisor's sign. This means 10 % -3 is 1, not -2 as some might expect. If you need a modulo that always returns a non-negative result, implement the adjustment.

Finally, be careful when using modulo with byte or short types. The result is promoted to int because of integer promotion rules. For example:

byte a = 200; byte b = 100; int result = a % b; // 0, but result is int, not byte

This can lead to implicit conversions and potential overflow if you try to assign the result back to a byte without a cast. Always store the result in an int or larger type unless you are certain the value fits.

c# modulo operator: Practical Usage and Code Examples | RYUSLOG DEV