c# modulus operator on floats

1.7k views Asked by At

The code below runs modulus 0.01 on the numbers 0.01, 0.02, 0.03, ... , 0.99

var testVals = Enumerable.Range(1, 99).Select(n => double.Parse("0." + n.ToString("D2")));
var moduloTest = testVals.Select(v => v % 0.01).ToList();

(note: parsing doubles from string is intentional, that way the only math operation made on the floats is the modulus)

I would expect the moduleTest list here to contain several floating point values that are very close to 0. However many of the values in the list are instead very close to 0.1.

I understand that floating point math is not exact, and introduces rounding errors but 0.1 here is not even close to 0.

2

There are 2 answers

1
chux - Reinstate Monica On BEST ANSWER

I understand that floating point math is not exact, but in many cases here it seems to be not even close.

Modulus here did not add error/inexactness, just made quite visible the effect of prior inexactness.


With a quality implementation of modulus, there is no inexactness. See Is fmod() exact when y is an integer?.

The issues lie with the assuming math with decimal values like 0.01 always behaves close to those values converted to the closest representable float. Finite float values are all exact. It is the operations that formed their values are where the "error" comes in - with some exceptions like modulus.

Printing values in hexadecimal or with sufficient decimal precision (like 17 significant decimal places) is often enough to show the expected 0.01 is not encoded as a float as 0.01, but a value near that. Do this with computed results too for greater insight.

The nature of a modulus operation is a sawtooth curve like the green line here.

Given the error generated in 0.01 conversion and /, (not modulus), the result of the modulus of OP's values are expectantly just on either side of the discontinuity: about 0.0 or near 0.01.

0
innominate227 On

I realized the answer as I was typing this. It does happen due to the inexactness of floats. Its just that for the modulus operation unlike most mathematical operations a small inexactness in the value can cause a large difference in the result.

0.03 for instance is most closely represented in floating point as something like 0.2999999999999999999 which if you were to do modulus 0.01 on would give you a number like 0.0999999999999