Детерминированная плавающая точка и .NET

Как я могу гарантировать, что вычисления с плавающей запятой в .NET-приложении (скажем, на С#) всегда дают один и тот же бит-точный результат? Особенно при использовании разных версий .NET и запуске на разных платформах (x86 vs x86_64). Неточности операций с плавающей запятой не имеют значения.

В Java я бы использовал strictfp. В языках C/С++ и других языках низкого уровня эта проблема существенно решена путем доступа к управляющим регистрам FPU/SSE, но это, вероятно, невозможно в .NET.

Даже с управлением регистром управления FPU JIT.NET будет генерировать другой код на разных платформах. Что-то вроде HotSpot было бы еще хуже в этом случае...

Зачем мне это нужно? Я думаю о написании игры в реальном времени (RTS), которая сильно зависит от быстрой математики с плавающей запятой и симуляции с блокировкой. По сути, я буду передавать только пользовательский ввод по сети. Это также относится к другим играм, которые реализуют повторы, сохраняя ввод пользователя.

Не вариант:

  • десятичные знаки (слишком медленные)
  • значения фиксированной точки (слишком медленные и громоздкие при использовании sqrt, sin, cos, tan, atan...)
  • Состояние обновления по сети, например, FPS: информация о местоположении для сотен или нескольких тысяч единиц не является вариантом

Любые идеи?

Ответ 1

Я не уверен в точном ответе на ваш вопрос, но вы можете использовать С++ и выполнять всю свою работу с float в dll С++, а затем возвращать результат в .Net через interopt.

Ответ 2

Результаты Bitexact для разных платформ - это боль в **. Если вы используете только x86, это не имеет значения, потому что FPU не изменяется от От 32 до 64 бит. Но проблема в том, что трансцендентные функции могут быть более точные на новых процессорах.

Четыре базовые операции не должны давать разные результаты, но ваша виртуальная машина может оптимизировать выражения и дать разные результаты. Так, как предположили муравьи, напишите свои подпрограммы add/mul/div/sub как неуправляемый код, чтобы быть в безопасности.

Для трансцендентных функций я боюсь, что вы должны использовать таблицу поиска гарантия точности бит. Рассчитайте результат, например. 4096 значений, сохранить их как константы, и если вам нужно значение между ними, интерполируйте. Это не дает вам большой точности, но это будет bitexact.

Ответ 3

Если вам нужен детерминизм с плавающей запятой, вам нужно устранить все переменные. Это возможно, если вы ограничите область действия.

  • Сделайте свое приложение только 64-разрядным. Таким образом, вам не нужно иметь дело с x87 против SSE (x86 JIT все еще испускает код x87 fp, другие JIT испускают SSE).
  • Используйте .NET Core и свяжите среду выполнения с вашим приложением. Таким образом, вы гарантируете одинаковый код для любой версии вашего приложения. Это имеет решающее значение, малейшие различия в кодегенах могут привести к тому, что вычисления fp будут давать разные результаты.
  • Придерживайтесь одной операционной системы. Серьезно, сколько кросс-платформ RTSes есть? Да, там Starcraft 2; что это AFAIK. Blizzard уже давно овладевает этим искусством, и очень немногие могут воспроизвести этот подвиг. Если вы действительно хотите поддерживать многопользовательский режим Mac-PC, вам придется тщательно проверить, что ваша среда выполнения .NET генерирует тот же код fp на обеих платформах. И если это не так, вам не повезло.

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