Back to Blog
C#

C# Modulo Operator: Syntax and Common Pitfalls

A practical look at the C# modulo operator: integer and floating-point behavior, negative operands, common pitfalls, and useful code examples.

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 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 built-in numeric types such as int, long, short, byte, float, double, and decimal. When the operand types differ, binary numeric promotion applies; for example, int % long is evaluated as long. For floating-point types, the remainder 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 non-negative remainder and the divisor is positive, adjust the result manually:

int Mod(int a, int b) // assumes b > 0 { 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

For float and double, the % operator follows the same sign rule as integer remainder: the result takes the sign of the dividend. It is not the same as Math.IEEERemainder, which rounds the quotient to the nearest integer. For example, 5.5 % 2.0 is 1.5, while Math.IEEERemainder(5.5, 2.0) is -0.5.

Precision issues can still 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 can be 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.

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

The out parameter receives the quotient. The out-parameter overload shown above has existed since .NET Framework 2.0 and remains available on later .NET platforms. If you only need the remainder, the % operator is simpler.

Performance and Precision Considerations

For integer types, the % operator typically compiles to a single CPU instruction on many common platforms, so it is 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 use 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 decimal precision rather than binary floating-point behavior, 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 int.MinValue % -1. The remainder is 0 because -1 divides every integer exactly, but the corresponding quotient int.MinValue / -1 is not representable in int. If your code computes both the quotient and remainder, or uses Math.DivRem, be aware of this division overflow. The same quotient overflow applies to long.MinValue / -1; the remainder is also 0.

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 zero = 0; int i = 10 % zero; // DivideByZeroException double zeroDouble = 0.0; double d = 10.0 % zeroDouble; // NaN decimal zeroDecimal = 0m; decimal m = 10m % zeroDecimal; // 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 operands are promoted to int before the operation, so the result is an int. For example:

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

In this example, the value is 0, but the expression's type is int. Assigning it back to a byte requires an explicit cast. For non-negative byte operands, the mathematical result always fits in a byte, but the cast is still required because the expression type is int. If you want to avoid unnecessary casts, store the result in an int or larger type.

C# Modulo Operator: Practical Usage and Code Examples | RYUSLOG DEV