Модуль дает неправильный результат?

Может ли кто-нибудь сказать мне, почему эти два расчетных модуля дают два разных результата? Мне просто нужно обвинять кого-то или кого-то, кроме меня за все те часы, которые я потерял, обнаружив эту ошибку.

public void test1()
{
    int stepAmount = 100;
    float t = 0.02f;
    float remainder = t % (1f / stepAmount);
    Debug.Log("Remainder: " + remainder);
    // Remainder: 0.01

    float fractions = 1f / stepAmount;
    remainder = t % fractions;
    Debug.Log("Remainder: " + remainder);
    // Remainder: 0
}

Использование VS-2017 V15.3.5

Ответы

Ответ 1

Моя лучшая ставка заключается в том, что из-за свободы среда выполнения должна выполнять операции с плавающей запятой с большей точностью, чем используемые типы, а затем усекать результат до точности типа при назначении:

Спецификация CLI в разделе 12.1.3 диктует точную точность чисел с плавающей запятой, float и double при использовании в местах хранения. Однако он позволяет превышать точность, когда числа с плавающей запятой используются в других местах, таких как стек выполнения, аргументы возвращают значения и т.д.... Какая точность используется для среды выполнения и базового оборудования. Эта дополнительная точность может привести к незначительным различиям в оценках с плавающей запятой между разными машинами или временем автономной работы.

Источник здесь.

В первом примере t % (1f / stepAmount) может выполняться полностью с более высокой точностью, чем float, а затем усекается, когда результат присваивается remainder, а во втором примере 1f / stepAmount усекается и назначается fractions до операции модуля.

Что касается того, почему создание stepamount a const делает обе операции модуля последовательными, причина в том, что 1f / stepAmount сразу становится постоянным выражением, которое оценивается и усекается, чтобы плавать точность во время компиляции и ничем не отличается от записи 0.01f, что по существу делает оба примера эквивалентными.