Почему оператор модуля не работает для double в С#?

Рассмотрим это:

double x,y;
x =120.0;
y = 0.05;

double z= x % y;

Я попробовал это и ожидал, что результат будет 0, но он вышел 0.04933333.

Однако

x =120.0;
y = 0.5;
double z= x % y;

действительно дал правильный результат 0.

Что здесь происходит?

Я пробовал Math.IEEERemainder(double, double), но он тоже не возвращал 0. Что здесь происходит?

Также, как в стороне, какой наиболее подходящий способ найти остаток в С#?

Ответ 1

Из-за своего формата хранения double не может хранить все значения точно так, как это было введено или отображено. Человеческое представление чисел обычно находится в десятичном формате, а double основаны на двойной системе.

В double, 120 хранится точно, потому что это целочисленное значение. Но 0.05 нет. Двойник приближается к ближайшему числу до 0.05, которое он может представлять. 0.5 является степенью 2 (1/2), поэтому его можно точно сохранить, и вы не получите ошибку округления.

Чтобы все номера точно совпадали с тем, как вы вводите/отображаете его в десятичной системе, используйте decimal вместо этого.

decimal x, y;
x = 120.0M;
y = 0.05M;

decimal z = x % y;  // z is 0

Ответ 2

Вы можете сделать что-то вроде:

double a, b, r;

a = 120;
b = .05;

r = a - Math.floor(a / b) * b;

Это должно помочь;)

Ответ 4

Я считаю, что если вы попытаетесь сделать то же самое с decimal, он будет работать правильно.

Ответ 5

Модуль должен использоваться только с целым числом. Остальная часть исходит от евклидовой дивизии. С двойным результатом могут быть неожиданные результаты.

См. в этой статье

Ответ 6

Это то, что мы используем.. :)

 public double ModuloOf(double v1, double v2)
    {
        var mult = 0;

        //find number of decimals
        while (v2 % 1 > 0)
        {
            mult++;
            v2 = v2 * 10;
        }

        v1 = v1 * Math.Pow(10, mult);

        var rem = v1 % v2;
        return rem / Math.Pow(10, mult);
    }