Детерминированная плавающая точка и.NET
Как я могу гарантировать, что вычисления с плавающей запятой в приложении.NET (скажем, в C#) всегда дают один и тот же точный битовый результат? Особенно при использовании разных версий.NET и на разных платформах (x86 против x86_64). Неточности операций с плавающей запятой не имеют значения.
В Java я бы использовал строгий fp. В C/C++ и других языках низкого уровня эта проблема в основном решается путем доступа к управляющим регистрам FPU / SSE, но это, вероятно, невозможно в.NET.
Даже с контролем регистра управления FPU JIT.NET будет генерировать разный код на разных платформах. Что-то вроде HotSpot было бы еще хуже в этом случае...
Зачем мне это нужно? Я думаю о написании стратегии в реальном времени (RTS), которая в большой степени зависит от быстрой математики с плавающей запятой вместе с симуляцией ступенчатой блокировки. По сути, я буду передавать только пользовательский ввод по сети. Это также относится к другим играм, которые реализуют повторы, сохраняя пользовательский ввод.
Не вариант являются:
- десятичные дроби (слишком медленно)
- значения с фиксированной точкой (слишком медленные и громоздкие при использовании sqrt, sin, cos, tan, atan...)
- обновлять состояние по сети, например, FPS: отправка информации о местоположении для сотен или нескольких тысяч единиц невозможна
Есть идеи?
3 ответа
Я не уверен в точном ответе на ваш вопрос, но вы могли бы использовать C++ и выполнить всю свою работу с плавающей точкой в d ++ C++, а затем вернуть результат в.Net через взаимодействие.
Результаты Bitexact для разных платформ - боль в а **. Если вы используете только x86, это не должно иметь значения, потому что FPU не меняется с 32 на 64 бита. Но проблема в том, что трансцендентные функции могут быть более точными на новых процессорах.
Четыре базовые операции не должны давать разные результаты, но ваша виртуальная машина может оптимизировать выражения, и это может давать разные результаты. Поэтому, как предложил Антс, напишите свои процедуры add/mul/div/sub как неуправляемый код, чтобы быть на безопасной стороне.
Боюсь, что для трансцендентных функций вы должны использовать справочную таблицу, чтобы гарантировать точность битов. Вычислите результат, например, из 4096 значений, сохраните их как константы и, если вам нужно значение между ними, интерполируйте. Это не даст вам большой точности, но это будет укус.
Если вам нужен детерминизм с плавающей точкой, вам нужно исключить все переменные. Это возможно, если вы несколько ограничите свою сферу.
- Сделайте ваше приложение только 64-битным. Таким образом, вам не нужно иметь дело с x87 против SSE (x86 JIT по-прежнему испускает код x87 fp, другие JIT испускают SSE).
- Используйте.NET Core и связывайте среду выполнения с вашим приложением. Таким образом, вы гарантируете один и тот же коден для любой версии вашего приложения. Это очень важно, малейшие различия в коде могут привести к тому, что вычисления fp дадут разные результаты.
- Придерживайтесь одной операционной системы. Серьезно, сколько кроссплатформенных RTS существует? Да, есть Starcraft 2; это все AFAIK. Blizzard уже давно овладевает этим искусством, и очень немногие могут повторить этот подвиг. Если вы действительно хотите поддерживать многопользовательский режим Mac-PC, вам придется тщательно проверить, что среда выполнения.NET генерирует один и тот же код fp на обеих платформах. И если это не так, вам не повезло.
Одна вещь, в которой я до сих пор не уверен, - можете ли вы доверять классу Math для получения последовательных результатов, учитывая вышеуказанные ограничения; но я полагаю, что так и должно быть.