Я много читал о детерминизме с плавающей запятой в .NET, то есть гарантирую, что тот же код с теми же входами даст одинаковые результаты на разных машинах. Поскольку .NET не имеет таких параметров, как Java fpstrict и MSVC fp: strict, похоже, что существует консенсус, что нет никакой возможности обойти эту проблему, используя чистый управляемый код. Игра С# AI Wars решила использовать математику с фиксированной точкой, но это громоздкое решение.
Основная проблема заключается в том, что CLR позволяет промежуточным результатам жить в регистрах FPU, которые имеют более высокую точность, чем собственная точность типа, что приводит к непредсказуемо более высоким результатам точности. Статья MSDN инженера CLR Дэвида Нотарио объясняет следующее:
Обратите внимание, что с текущей спецификацией его выбор по-прежнему зависит от языка "Предсказуемость. Язык может вставлять conv.r4 или conv.r8 инструкции после каждой операции FP, чтобы получить" предсказуемое поведение".Очевидно, что это действительно дорого, а на разных языках разные компромиссы. С#, например, ничего не делает, если вы хотите сужение, вам придется вручную вставлять (float) и (double) броски.
Это говорит о том, что можно добиться детерминизма с плавающей запятой, просто вставив явные приведения для каждого выражения и подвыражения, которое вычисляет float. Можно написать тип оболочки вокруг float для автоматизации этой задачи. Это было бы простым и идеальным решением!
Другие комментарии однако предполагают, что это не так просто. Эрик Липперт недавно заявил (внимание мое):
в некоторой версии среды выполнения, приведение в float явно дает другой результат, чем не делать этого. Когда вы явно бросаете float, компилятор С# дает подсказку для среды выполнения, чтобы сказать: "Возьмите эту вещь из режима сверхвысокой точности, если вы используете этот оптимизация".
Что это за "намек" на время выполнения? Указывает ли спецификация С#, что явное приведение в float вызывает вложение conv.r4 в IL? Определяет ли спецификация CLR, что команда conv.r4 приводит к сужению значения до его собственного размера? Только если оба они являются истинными, мы можем полагаться на явные приведения, чтобы обеспечить "предсказуемость" с плавающей запятой, как объяснил Дэвид Нотарио.
Наконец, даже если мы действительно можем принудить все промежуточные результаты к размеру собственного типа, достаточно ли этого, чтобы гарантировать воспроизводимость на разных машинах или существуют другие факторы, такие как настройки времени выполнения FPU/SSE?