Search code examples
c#floating-pointmodulomodulus

c# modulus operator on floats


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.


Solution

  • 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.