Согласна ли математика с плавающей точкой в ​​C #? Может ли это быть?

155

Нет, это не другой вопрос «Почему (1 / 3.0) * 3! = 1» .

В последнее время я много читаю о числах с плавающей запятой; в частности, как один и тот же расчет может дать разные результаты для разных архитектур или настроек оптимизации.

Это проблема для видеоигр, в которых хранятся повторы или они работают в одноранговой сети (в отличие от сервера-клиента), в которых все клиенты получают одинаковые результаты при каждом запуске программы - небольшое расхождение в одном Вычисление с плавающей точкой может привести к совершенно разному игровому состоянию на разных машинах (или даже на одной машине! )

Это происходит даже среди процессоров, которые «следуют» IEEE-754 , главным образом потому, что некоторые процессоры (а именно x86) используют двойную расширенную точность . То есть они используют 80-битные регистры для выполнения всех вычислений, а затем усекаются до 64- или 32-битных, что приводит к результатам округления, отличным от тех, которые используют 64- или 32-битные для расчетов.

Я видел несколько решений этой проблемы в Интернете, но все для C ++, а не для C #:

  • Отключите режим двойной расширенной точности (чтобы во всех doubleвычислениях использовался IEEE-754 64 -битный режим ) с использованием _controlfp_s(Windows), _FPU_SETCW(Linux?) Или fpsetprec(BSD).
  • Всегда запускайте один и тот же компилятор с одинаковыми настройками оптимизации и требуйте, чтобы все пользователи имели одинаковую архитектуру ЦП (без межплатформенного воспроизведения). Поскольку мой «компилятор» на самом деле является JIT, который может оптимизироваться по-разному при каждом запуске программы , я не думаю, что это возможно.
  • Используйте арифметику с фиксированной запятой, избегайте floatи doubleвообще. decimalбудет работать для этой цели, но будет намного медленнее, и ни одна из System.Mathфункций библиотеки не поддерживает его.

Так это вообще проблема в C #? Что если я собираюсь поддерживать только Windows (не Mono)?

Если это так, есть ли способ заставить мою программу работать с нормальной двойной точностью?

Если нет, то есть ли библиотеки, которые помогли бы поддерживать согласованность вычислений с плавающей точкой?

BlueRaja - Дэнни Пфлугхофт
источник
Я видел этот вопрос , но каждый ответ либо повторяет проблему без решения, либо говорит: «игнорируйте это», что не вариант. Я задал похожий вопрос на gamedev , но (из-за аудитории) большинство ответов, похоже, ориентированы на C ++.
BlueRaja - Дэнни Пфлюгофт
1
не ответ, но я уверен, что в большинстве доменов вы могли бы спроектировать свою систему таким образом, чтобы все общие состояния были детерминированными, и из-за этого не произошло существенного
снижения производительности
1
@ Петр, ты знаешь о какой-нибудь быстрой эмуляции с плавающей точкой для .net?
CodesInChaos
1
Java страдает от этой проблемы?
Джош
3
@Josh: Java имеет strictfpключевое слово, которое заставляет все вычисления выполняться в указанном размере ( floatили double), а не в расширенном размере. Однако у Java все еще много проблем с поддержкой IEE-754. Очень (очень, очень) немногие языки программирования хорошо поддерживают IEE-754.
Porges

Ответы:

52

Я не знаю способа сделать нормальные плавающие точки детерминированными в .net. JITter разрешено создавать код, который ведет себя по-разному на разных платформах (или между разными версиями .net). Поэтому использование нормальных floats в детерминированном .net-коде невозможно.

Обходные пути, которые я рассмотрел:

  1. Реализация FixedPoint32 в C #. Хотя это не так уж сложно (у меня есть половина готовой реализации), очень маленький диапазон значений делает его раздражающим в использовании. Вы должны быть осторожны всегда, чтобы не переполнять и не терять слишком много точности. В конце концов я обнаружил, что это не проще, чем использовать целые числа напрямую.
  2. Реализация FixedPoint64 в C #. Я нашел это довольно сложно сделать. Для некоторых операций были бы полезны промежуточные целые числа из 128 бит. Но .net не предлагает такого типа.
  3. Реализуйте пользовательскую 32-битную плавающую точку. Отсутствие встроенного BitScanReverse вызывает некоторые неудобства при реализации этого. Но в настоящее время я считаю, что это самый перспективный путь.
  4. Используйте собственный код для математических операций. Вносит накладные расходы на вызов делегата при каждой математической операции.

Я только начал программную реализацию 32-битной математики с плавающей запятой. Он может делать около 70 миллионов операций добавления / умножения в секунду на моем 2,66 ГГц i3. https://github.com/CodesInChaos/SoftFloat . Очевидно это все еще очень неполно и глючит.

CodesInChaos
источник
2
есть BigInteger «неограниченного» размера, хотя и не такой быстрый, как встроенный int или long, поэтому .NET предлагает такой тип (я думаю, он создан для F #, но может использоваться в C #)
Rune FS
Другой вариант - оболочка GNU MP для .NET . Это оболочка для библиотеки GNU Multiple Precision, которая поддерживает «бесконечные» прецизионные целые числа, рациональные числа (дроби) и числа с плавающей запятой.
Коул Джонсон
2
Если вы собираетесь сделать что-либо из этого, вы можете попробовать decimalсначала, так как это гораздо проще сделать. Только если он будет слишком медленным для поставленной задачи, стоит подумать о других подходах.
Роман Старков
Я узнал об одном особом случае, когда плавающие точки являются детерминированными. Объяснение, которое я получил, таково: для умножения / деления, если одно из чисел FP является степенью двух чисел (2 ^ x), значимое / мантисса не изменится во время вычисления. Изменится только экспонента (точка сместится). Так что округления никогда не произойдет. Результат будет детерминированным.
зигзаг
Пример: число типа 2 ^ 32 представляется как (экспонента: 32, мантисса: 1). Если мы умножим это на другое число с плавающей точкой (exp, man), результат будет (exp + 32, man * 1). Для деления результат равен (экспо - 32, человек * 1). Умножение мантиссы на 1 не меняет мантиссу, поэтому не имеет значения, сколько в ней битов.
зигзаг
28

Спецификация C # (§4.1.6 типы с плавающей запятой), в частности, позволяет выполнять вычисления с плавающей запятой, используя точность, превышающую точность результата. Так что нет, я не думаю, что вы можете сделать эти расчеты детерминированными непосредственно в .Net. Другие предлагали различные обходные пути, чтобы вы могли их попробовать.

svick
источник
9
Я только что понял, что спецификация C # на самом деле не имеет значения, если кто-то распространяет скомпилированные сборки. Это имеет значение, только если кто-то хочет совместимости с исходным кодом. Что действительно важно, так это спецификация CLR. Но я уверен, что гарантии такие же слабые, как гарантии C #.
CodesInChaos
Не будет ли приведение к doubleкаждому разу после операции убирать ненужные биты, давая последовательные результаты?
IllidanS4 хочет вернуть Монику
2
@ IllidanS4 Не думаю, что это обеспечит стабильные результаты.
svick
14

Следующая страница может быть полезна в случае, когда вам нужна абсолютная переносимость таких операций. В нем обсуждается программное обеспечение для тестирования реализаций стандарта IEEE 754, включая программное обеспечение для эмуляции операций с плавающей запятой. Однако большая часть информации, вероятно, относится к C или C ++.

http://www.math.utah.edu/~beebe/software/ieee/

Примечание о фиксированной точке

Двоичные числа с фиксированной запятой также могут хорошо заменить плавающую точку, как это видно из четырех основных арифметических операций:

  • Сложение и вычитание тривиальны. Они работают так же, как целые числа. Просто добавьте или вычтите!
  • Чтобы умножить два числа с фиксированной запятой, умножьте два числа, затем сдвиньте вправо на определенное количество дробных битов.
  • Чтобы разделить два числа с фиксированной запятой, сдвиньте делимое влево на определенное количество дробных битов, а затем разделите на делитель.
  • В четвертой главе этой статьи есть дополнительные указания по реализации двоичных чисел с фиксированной запятой.

Двоичные числа с фиксированной запятой могут быть реализованы для любого целочисленного типа данных, такого как int, long и BigInteger, а также для не-CLS-совместимых типов uint и ulong.

Как предлагается в другом ответе, вы можете использовать справочные таблицы, где каждый элемент в таблице является двоичным числом с фиксированной запятой, чтобы помочь реализовать сложные функции, такие как синус, косинус, квадратный корень и т. Д. Если таблица поиска менее детализирована, чем число с фиксированной точкой, рекомендуется округлить входные данные, добавив половину гранулярности таблицы поиска к входным данным:

// Assume each number has a 12 bit fractional part. (1/4096)
// Each entry in the lookup table corresponds to a fixed point number
//  with an 8-bit fractional part (1/256)
input+=(1<<3); // Add 2^3 for rounding purposes
input>>=4; // Shift right by 4 (to get 8-bit fractional part)
// --- clamp or restrict input here --
// Look up value.
return lookupTable[input];
Питер О.
источник
5
Вы должны загрузить это на сайт проекта с открытым исходным кодом, например, sourceforge или github. Это облегчает поиск, облегчает внесение вклада, облегчает добавление в ваше резюме и т. Д. Кроме того, несколько советов по исходному коду (не стесняйтесь игнорировать): используйте constвместо staticконстант, чтобы компилятор мог их оптимизировать; предпочитаю функции-члены статическим функциям (чтобы мы могли вызывать, например, myDouble.LeadingZeros()вместо IntDouble.LeadingZeros(myDouble)); старайтесь избегать однобуквенных имен переменных ( MultiplyAnyLengthнапример, имеет 9, что делает его очень сложным для отслеживания)
BlueRaja - Дэнни Пфлугхофт
Будьте осторожны, используя uncheckedне-CLS-совместимые типы, такие как ulong, uintи т. Д. Для целей скорости - поскольку они используются так редко, JIT не оптимизирует их так агрессивно, поэтому их использование может быть медленнее, чем использование обычных типов, таких как longи int. Кроме того, в C # есть перегрузка операторов , от которой этот проект сильно выиграл бы. Наконец, есть ли связанные юнит-тесты? Помимо этих маленьких вещей, потрясающая работа Питера, это смехотворно впечатляет!
BlueRaja - Дэнни Пфлюгофт
Спасибо за комментарии. Я выполняю модульные тесты кода. Они довольно обширны, но слишком обширны, чтобы выпускать их сейчас. Я даже пишу вспомогательные процедуры модульного тестирования, чтобы упростить написание нескольких тестов. Сейчас я не использую перегруженные операторы, потому что у меня есть планы по переводу кода на Java, когда я закончу.
Питер О.
2
Самое смешное, что когда я писал в вашем блоге, я не заметил, что этот блог принадлежит вам. Я только что решил попробовать Google +, и в его C # искра он предложил эту запись в блоге. Поэтому я подумал: «Какое замечательное совпадение для нас двоих, чтобы начать писать такие вещи одновременно». Но, конечно, у нас был тот же триггер :)
CodesInChaos
1
Зачем переносить это на Java? Java уже имеет гарантированную детерминированную математику с плавающей точкой через strictfp.
Сурьма
9

Это проблема для C #?

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

Могу ли я использовать System.Decimal?

Там нет причин, вы не можете, однако это собака медленно.

Есть ли способ заставить мою программу работать с двойной точностью?

Да. Хостинг среды CLR самостоятельно ; и скомпилировать все необходимые вызовы / флаги (которые изменяют поведение арифметики с плавающей запятой) в приложение C ++ перед вызовом CorBindToRuntimeEx.

Существуют ли какие-либо библиотеки, которые помогли бы поддерживать согласованность вычислений с плавающей запятой?

Не то, что я знаю о.

Есть ли другой способ решить эту проблему?

Я занимался этой проблемой раньше, идея в том, чтобы использовать QNumbers . Они являются формой реальных событий с фиксированной точкой; но не фиксированная точка в base-10 (десятичная) - скорее base-2 (двоичная); из-за этого математические примитивы на них (add, sub, mul, div) намного быстрее, чем наивные фиксированные точки base-10; особенно если nэто одинаково для обоих значений (что в вашем случае будет). Кроме того, поскольку они являются интегральными, они имеют четко определенные результаты на каждой платформе.

Имейте в виду, что частота кадров все еще может влиять на них, но она не так плоха, и ее легко исправить с помощью точек синхронизации.

Могу ли я использовать больше математических функций с QNumbers?

Да, туда и обратно десятичная дробь, чтобы сделать это. Кроме того, вы действительно должны использовать справочные таблицы для функций trig (sin, cos); поскольку они действительно могут давать разные результаты на разных платформах - и если вы правильно их кодируете, они могут напрямую использовать QNumbers.

Джонатан Дикинсон
источник
3
Не уверен, что вы говорите с частотой кадров. Ясно, что вы хотели бы иметь фиксированную частоту обновления (см., Например, здесь ) - независимо от того, соответствует ли это частоте кадров дисплея, не имеет значения. Пока неточности одинаковы на всех машинах, мы хороши. Я совсем не понимаю ваш третий ответ.
BlueRaja - Дэнни Пфлюгофт
@BlueRaja: Ответ "Есть ли способ заставить мою программу работать с двойной точностью?" это будет означать либо повторную реализацию всего Common Language Runtime, что было бы чрезвычайно сложно, либо использование собственных вызовов C ++ DLL из приложения C #, как намекало в ответе пользователя shelleybutterfly. Думайте о «QNumbers» просто как о двоичных числах с фиксированной точкой, как подсказывалось в моем ответе (до сих пор я не видел, чтобы двоичные числа с фиксированной точкой назывались «QNumbers».)
Питер О.
@ Питер О. Вам не нужно переопределять время выполнения. Сервер, на котором я работаю в своей компании, размещает среду выполнения CLR как собственное приложение C ++ (как и SQL Server). Я предлагаю вам Google CorBindToRuntimeEx.
Джонатан Дикинсон
@BlueRaja это зависит от рассматриваемой игры. Применение шагов с фиксированной частотой кадров ко всем играм не является жизнеспособным вариантом - поскольку алгоритм AOE вводит искусственную задержку; что недопустимо, например, в FPS.
Джонатан Дикинсон
1
@Jonathan: Это проблема только в одноранговых играх, которые отправляют только входные данные - для этого вам нужно иметь фиксированную частоту обновления. Большинство FPS не работают таким образом, но те немногие, которые обязательно имеют фиксированную частоту обновления. Смотрите этот вопрос .
BlueRaja - Дэнни Пфлюгофт
6

Согласно этой немного старой записи в блоге MSDN, JIT не будет использовать SSE / SSE2 для плавающей запятой, это все x87. Из-за этого, как вы упомянули, вам нужно беспокоиться о режимах и флагах, а в C # это невозможно контролировать. Таким образом, использование обычных операций с плавающей запятой не гарантирует одинаковый результат на каждой машине для вашей программы.

Чтобы получить точную воспроизводимость двойной точности, вам нужно будет выполнить программную эмуляцию с плавающей запятой (или с фиксированной запятой). Я не знаю библиотек C #, чтобы сделать это.

В зависимости от операций, которые вам нужны, вы можете уйти с единой точностью. Вот идея:

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

Большая проблема с x87 заключается в том, что вычисления могут выполняться с 53-битной или 64-битной точностью в зависимости от флага точности и от того, был ли регистр передан в память. Но для многих операций выполнение операции с высокой точностью и округление до более низкой точности гарантирует правильный ответ, что означает, что ответ будет гарантированно одинаковым во всех системах. Получите ли вы дополнительную точность, не имеет значения, поскольку у вас достаточно точности, чтобы гарантировать правильный ответ в любом случае.

Операции, которые должны работать в этой схеме: сложение, вычитание, умножение, деление, sqrt. Такие вещи, как грех, опыт и т. Д. Не сработают (результаты обычно совпадают, но гарантии нет). "Когда двойное округление безобидно?" Справочник ACM (оплаченный рег. Запрос)

Надеюсь это поможет!

Натан Уайтхед
источник
2
Также проблема в том, что .NET 5, или 6, или 42, возможно, больше не используют режим вычисления x87. В стандарте нет ничего, что требовало бы этого.
Эрик Дж.
5

Как уже говорилось в других ответах: да, это проблема в C # - даже при использовании чистой Windows.

Что касается решения: вы можете уменьшить (и с некоторым усилием / ударом по производительности) полностью избежать проблемы, если вы используете встроенный BigIntegerкласс и масштабируете все вычисления с определенной точностью, используя общий знаменатель для любого вычисления / хранения таких чисел.

В соответствии с просьбой OP - относительно производительности:

System.Decimalпредставляет число с 1 битом для знака и 96-битное целое число и «масштаб» (представляющий, где находится десятичная точка). Для всех вычислений, которые вы делаете, он должен работать с этой структурой данных и не может использовать никакие инструкции с плавающей запятой, встроенные в ЦП.

BigInteger«Решение» делает нечто подобное - только то , что вы можете определить , сколько цифр вам нужно / хочу ... возможно , вы хотите только 80 бит или 240 бит точности.

Медлительность всегда возникает из-за необходимости симулировать все операции с этими числами с помощью только целочисленных инструкций без использования встроенных инструкций CPU / FPU, что, в свою очередь, приводит к гораздо большему количеству инструкций на математическую операцию.

Чтобы уменьшить удар по производительности, есть несколько стратегий - например, QNumbers (см. Ответ Джонатана Дикинсона - Соответствует ли математика с плавающей запятой в C #? Может ли это быть? ) И / или кэширование (например, вычисления триггеров ...) и т. Д.

Яхья
источник
1
Обратите внимание, что BigIntegerдоступно только в .Net 4.0.
svick
Я предполагаю, что снижение производительности BigIntegerпревышает даже снижение производительности десятичным.
CodesInChaos
Несколько раз в ответах здесь есть ссылка на Decimalснижение производительности при использовании (@Jonathan Dickinson - «медлительность собаки») или BigInteger(комментарий @CodeInChaos выше) - может кто-нибудь дать небольшое объяснение этим ударам производительности и относительно того, / почему они действительно мешают решению проблемы.
Барри Кей
@Yahia - спасибо за редактирование - интересное чтение, однако, не могли бы вы также дать приблизительное предположение о том, что производительность неиспользования 'float' мы говорим на 10% медленнее или в 10 раз медленнее - я просто хочу получить представление о предполагаемой величине.
Барри Кей
это скорее похоже в районе 1: 5, чем «всего 10%»
Yahia
2

Что ж, вот моя первая попытка сделать это :

  1. Создайте проект ATL.dll, в котором есть простой объект, который будет использоваться для ваших критических операций с плавающей запятой. не забудьте скомпилировать его с флагами, которые отключают использование любого оборудования, отличного от xx87, для выполнения операций с плавающей запятой.
  2. Создайте функции, которые вызывают операции с плавающей запятой и возвращают результаты; начните с простого, а затем, если он работает для вас, вы всегда можете увеличить сложность, чтобы при необходимости удовлетворить ваши требования к производительности.
  3. Разместите вызовы control_fp вокруг фактической математики, чтобы убедиться, что она выполняется одинаково на всех машинах.
  4. Ссылайтесь на вашу новую библиотеку и тестируйте, чтобы убедиться, что она работает как положено

(Я полагаю, что вы можете просто скомпилировать 32-битный файл .dll, а затем использовать его с x86 или AnyCpu [или, скорее всего, только для x86 в 64-битной системе; см. Комментарий ниже].)

Затем, если это сработает, если вы захотите использовать Mono, я полагаю, что вы сможете реплицировать библиотеку на другие платформы x86 аналогичным образом (конечно, не COM; хотя, возможно, с вином? мы идем туда, хотя ...).

Предполагая, что вы можете заставить его работать, вы должны иметь возможность настраивать пользовательские функции, которые могут выполнять несколько операций одновременно, чтобы исправить любые проблемы с производительностью, и у вас будет математика с плавающей запятой, которая позволяет получать согласованные результаты на разных платформах с минимальным количеством кода, написанного на C ++, и оставшаяся часть кода на C #.

shelleybutterfly
источник
«Компилировать в 32-битный .dll, а затем использовать ... AnyCpu» Я думаю, что это будет работать только при работе в 32-битной системе. В 64-битной системе только целевая программа x86сможет загрузить 32-битную DLL.
CodesInChaos
2

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

Стратегия, которую я бы принял, по сути такова:

  • Используйте более медленный (если это необходимо; если есть более быстрый способ - отлично!), Но предсказуемый метод, чтобы получить воспроизводимые результаты
  • Используйте double для всего остального (например, рендеринг)

Суть в том, что вам нужно найти баланс. Если вы тратите 30 мс на рендеринг (~ 33 к / с) и только 1 мс на обнаружение коллизий (или вставляете какую-то другую высокочувствительную операцию) - даже если вы утраиваете время, необходимое для выполнения критической арифметики, это влияет на частоту кадров. Вы снижаетесь с 33,3 кадр / с до 30,3 кадр / с.

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

Брайан Ванденберг
источник
1

Проверка ссылок в других ответах дает понять, что у вас никогда не будет гарантии, «правильно» ли реализована плавающая точка, или вы всегда будете получать определенную точность для данного вычисления, но, возможно, вы могли бы приложить максимум усилий, (1) усечение всех вычислений до общего минимума (например, если разные реализации дадут вам точность от 32 до 80 бит, всегда обрезая каждую операцию до 30 или 31 бит), (2) иметь таблицу нескольких тестовых примеров при запуске (граничные случаи сложения, вычитания, умножения, деления, sqrt, косинуса и т. д.), и если реализация вычисляет значения, соответствующие таблице, тогда не стоит вносить какие-либо корректировки.

Майк
источник
всегда обрезать каждую операцию до 30 или 31 бита - это именно то, что floatделает тип данных на компьютерах с архитектурой x86, - однако это приведет к немного отличающимся результатам от машин, которые выполняют все свои вычисления с использованием только 32-битных данных, и эти небольшие изменения будут распространяться со временем. Отсюда и вопрос.
BlueRaja - Дэнни Пфлюгофт
Если «N битов точности» означает, что любой расчет точен с таким количеством битов, а машина A с точностью до 32 бит, а машина B с точностью до 48 бит, то первые 32 бита любого вычисления на обеих машинах должны быть идентичны. Не будет ли усечение до 32 бит или меньше после каждой операции точно синхронизировать обе машины? Если нет, то какой пример?
Свидетельство защиты ID 44583292
-3

Ваш вопрос в довольно сложных и технических вещах О_о. Однако у меня может быть идея.

Вы уверены, что процессор выполняет некоторые настройки после любых операций с плавающей запятой. И CPU предлагают несколько разных инструкций, которые выполняют разные операции округления.

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

«Ошибки», допущенные при корректировке округления, будут увеличиваться при каждой дальнейшей инструкции.

В качестве примера можно сказать, что на уровне сборки: a * b * c не эквивалентен a * c * b.

Я не совсем уверен в этом, вам нужно будет попросить кого-то, кто знает архитектуру процессора намного больше, чем я: p

Однако, чтобы ответить на ваш вопрос: в C или C ++ вы можете решить свою проблему, потому что у вас есть некоторый контроль над машинным кодом, сгенерированным вашим компилятором, однако в .NET у вас его нет. Поэтому, пока ваш машинный код может отличаться, вы никогда не будете уверены в точном результате.

Мне любопытно, каким образом это может быть проблемой, потому что вариация кажется очень минимальной, но если вам нужна действительно точная работа, единственное решение, о котором я могу подумать, - это увеличить размер ваших плавающих регистров. Используйте двойную точность или даже длинную двойную, если можете (не уверен, что это возможно с помощью CLI).

Я надеюсь, что я был достаточно ясен, я не совершенен в английском (... вообще: s)

AxFab
источник
9
Представьте себе P2P-шутер. Ты стреляешь в парня, ты ударил его, и он умирает, но это очень близко, ты почти пропустил. На компьютере другого парня используются немного другие вычисления, и он вычисляет, что вы пропустили. Вы видите проблему сейчас? В этом случае увеличение размера регистров не поможет (по крайней мере, не полностью). Используя точно такой же расчет на каждом компьютере, будет.
свик
5
В этом сценарии один обычно не заботится о том , как близко результат фактического результата ( до тех пор , как это разумно), но важно то , что это точно то же самое для всех пользователей.
CodesInChaos
1
Вы правы, я не думал об этом сценарии. Однако я согласен с @CodeInChaos по этому вопросу. Я не нашел это действительно умным, чтобы принять важное решение дважды. Это больше проблема архитектуры программного обеспечения. Одна программа, заявка стрелка на исключение, должна сделать расчет и отправить результат другим. У вас никогда не будет ошибок таким образом. У вас есть удар или нет, но только один принять решение. Как сказать @driushkin
AxFab
2
@ Aesgar: Да, именно так работает большинство стрелков; этот «авторитет» называется сервером, и мы называем общую архитектуру архитектурой «клиент / сервер». Однако существует другая разновидность архитектуры: одноранговая. В P2P нет сервера; скорее, все клиенты должны проверять все действия друг с другом, прежде чем что-либо произойдет. Это увеличивает задержку, делая ее неприемлемой для стрелков, но значительно уменьшает сетевой трафик, что делает его идеальным для игр, где допустимо небольшое отставание (~ 250 мс), но синхронизация всего игрового состояния невозможна. А именно, в играх RTS, таких как C & C и Starcraft, используется P2P.
BlueRaja - Дэнни Пфлюгофт
5
В p2p-игре у вас нет надежной машины, на которую можно положиться. Если вы позволяете одной станции решать, ударила ли его пуля или нет, вы открываете возможность обмана клиента. Кроме того, ссылки не могут даже обрабатывать объем данных, который иногда получается - игры работают, отправляя заказы, а не результаты. Я играю в игры RTS, и много раз я видел, как столько хлама пролетало, что его нельзя было отправить по обычным домашним каналам связи.
Лорен Печтел