Чи відповідає математика з плаваючою комою в C #? Чи може бути?


155

Ні, це не інше питання "Чому це (1 / 3.0) * 3! = 1" .

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

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

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

Я бачив декілька рішень цієї проблеми в Інтернеті, але все для C ++, а не для C #:

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

Отже, це навіть проблема в C #? Що робити, якщо я маю намір лише підтримувати Windows (не Mono)?

Якщо це так, чи є який-небудь спосіб змусити мою програму працювати у звичайній подвійній точності?

Якщо ні, то чи існують бібліотеки, які допомогли б підтримувати обчислення з плаваючою комою?


Я бачив це питання , але кожна відповідь або повторює проблему, не маючи рішення, або каже "ігноруй це", що не є варіантом. Я задавав подібне запитання щодо gamedev , але (через аудиторію) більшість відповідей, здається, орієнтовані на C ++.
BlueRaja - Danny Pflughoeft

1
не відповідь, але я впевнений, що ви в більшості доменів змогли спроектувати свою систему таким чином, щоб усі спільні стану були детермінованими, і через це не було значної погіршення продуктивності
driushkin

1
@Peter Ви знаєте про будь-яку швидку емуляцію з плаваючою точкою для .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-бітної математики з плаваючою комою. На моєму 2,66 ГГц i3 він може робити близько 70 мільйонів доповнень / множень за секунду. https://github.com/CodesInChaos/SoftFloat . Очевидно, це все ще дуже неповно і баггі.


2
є «необмежений» розмір цілого числа доступних BigInteger , хоча і не так швидко , як рідний міжнар або довго він там так .NET робить пропозицію такого типу (створений для F # Я вважаю , але можу бути використаний в C #)
руна FS

Інший варіант - обгортка GNU MP для .NET . Це обгортка навколо Бібліотеки множинної точності GNU, яка підтримує "нескінченні" точні цілі числа, раціональні (дроби) та числа з плаваючою комою.
Коул Джонсон

2
Якщо ви збираєтеся робити щось із цього, ви можете спробувати decimalспочатку, оскільки це зробити набагато простіше. Тільки якщо це занадто повільно для вирішення поставленого завдання, варто було б подумати про інші підходи.
Роман Старков

Я дізнався про один особливий випадок, коли плаваючі точки детерміновані. Пояснення, яке я отримав: Для множення / ділення, якщо одне з чисел FP є силою двох чисел (2 ^ x), значення / mantissa не зміниться під час обчислення. Зміниться лише показник (точка переміститься). Тож округлення ніколи не відбудеться. Результат буде детермінованим.
зигзаг

Приклад: Число на зразок 2 ^ 32 представлено у вигляді (показник: 32, mantissa: 1). Якщо ми помножимо це на інший поплавок (exp, man), результат - (exp + 32, man * 1). Для поділу результат є (expo - 32, man * 1). Помноження мантіси на 1 не змінює мантісу, тому не має значення, скільки бітів у неї є.
зигзаг

28

Специфікація C # (§4.1.6 типів з плаваючою точкою) спеціально дозволяє проводити обчислення з плаваючою комою, використовуючи точність, вищу, ніж результат. Отже, ні, я не думаю, що ви можете зробити ці розрахунки детермінованими безпосередньо у .Net. Інші пропонували різні способи вирішення, тож ви можете спробувати їх.


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
однолітерних

Будьте обережні, використовуючи uncheckedта не сумісні з CLS типи типу ulong, uintтощо, для швидкісних цілей - оскільки вони так рідко використовуються, JIT не оптимізує їх настільки агресивно, тому їх використання насправді може бути повільніше, ніж використання звичайних типів, таких як longі int. Крім того, у C # є перевантаження оператора , від якого цей проект отримав би велику користь. Нарешті, чи є пов'язані одиничні тести? Крім цих дрібниць, дивовижної роботи Пітера, це смішно вражає!
BlueRaja - Danny Pflughoeft

Дякую за коментарі. Я виконую одиничні тести на код. Вони є доволі обширними, хоча зараз занадто обширними, щоб випустити. Я навіть пишу допоміжні процедури для полегшення написання декількох тестів. Я зараз не використовую перевантажених операторів, оскільки у мене є плани перекладати код на Java, коли я закінчу.
Петро О.

2
Найцікавіше, що коли я розміщував у вашому блозі, я не помітив, що ваш блог був. Я щойно вирішив спробувати google + і в його іскрі C # він запропонував цю запис у блозі. Тому я подумав «Який чудовий збіг для нас двох, щоб почати писати таку річ одночасно». Але, звичайно, у нас був той самий тригер :)
CodesInChaos

1
Навіщо турбуватися переносом цього на Java? Java вже має гарантовану детерміновану математику з плаваючою точкою через strictfp.
Сурма

9

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

Так. Різні архітектури - це найменше занепокоєння, різні рамки тощо можуть призвести до відхилень через неточності в представленнях поплавця - навіть якщо вони однакові неточності (наприклад, однакова архітектура, за винятком більш повільної GPU на одній машині).

Чи можу я використовувати System.Decimal?

Немає причини, чому не можна, однак собака повільна.

Чи є спосіб змусити мою програму працювати в подвійній точності?

Так. Приймайте час виконання CLR самостійно ; і компілювати всі несезарні виклики / прапори (які змінюють поведінку арифметики з плаваючою комою) у додаток C ++ перед тим, як викликати CorBindToRuntimeEx.

Чи є бібліотеки, які допомогли б підтримувати розрахунки з плаваючою комою послідовними?

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

Чи є інший спосіб вирішити це?

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

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

Чи можу я використовувати більше математичних функцій за допомогою QNumbers?

Так, зворотній шлях десяткової для цього. Крім того, ви дійсно повинні використовувати таблиці пошуку для функцій trig (sin, cos); оскільки вони дійсно можуть давати різні результати на різних платформах - і якщо ви правильно їх кодуєте, вони можуть використовувати QNumbers безпосередньо.


3
Не впевнений, про що ви говорите, коли виникають проблеми з рамками. Зрозуміло, що ви хочете мати фіксовану швидкість оновлення (див. Приклад тут ) - незалежно від того, чи є вона такою ж, як частота кадрів дисплея, не має значення. Поки неточності однакові на всіх машинах, ми добре. Я взагалі не розумію вашої третьої відповіді.
BlueRaja - Danny Pflughoeft

@BlueRaja: Відповідь "Чи є спосіб змусити мою програму працювати в подвійній точності?" може означати повторне виконання всього загального мовного режиму виконання, що було б надзвичайно складним, або використання рідних дзвінків на DLL C ++ з додатку C #, як натякає на відповідь користувача shelleybutterfly. Подумайте про "QNumbers" лише як про двійкові цифри з фіксованою точкою, як натякає у моїй відповіді (я до цього часу не бачив двійкових цифр з фіксованою точкою, які називаються "QNumbers".)
Пітер О.

@ Pieter O. Вам не потрібно повторно виконувати виконання. Сервер, на якому я працюю у своїй компанії, розміщує час виконання CLR як нативну програму C ++ (як і SQL Server). Я пропоную вам google CorBindToRuntimeEx.
Джонатан Дікінсон

@BlueRaja це залежить від гри, про яку йдеться. Застосування фіксованих кадрів до всіх ігор не є можливим варіантом - адже алгоритм AOE вводить штучну затримку; що неприпустимо, наприклад, у FPS.
Джонатан Дікінсон

1
@Jonathan: Це лише проблема в однорангових іграх, які надсилають лише вхідні дані - для них потрібно мати фіксовану швидкість оновлення. Більшість FPS не працюють так, але деякі з них обов'язково мають фіксовану швидкість оновлення. Дивіться це питання .
BlueRaja - Danny Pflughoeft

6

Відповідно до цього трохи старого запису в блозі MSDN, JIT не використовуватиме SSE / SSE2 для плаваючої точки, це все x87. Через це, як ви вже згадували, ви повинні турбуватися про режими та прапори, а в C # це неможливо контролювати. Таким чином, використання звичайних операцій з плаваючою комою не гарантуватиме точно такий же результат на кожній машині для вашої програми.

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

Залежно від операцій, які вам потрібні, ви зможете піти з однією точністю. Ось ідея:

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

Велика проблема з x87 полягає в тому, що обчислення можна проводити з 53-бітною або 64-бітовою точністю, залежно від прапора точності та того, чи зареєструвався регистр в пам'ять. Але для багатьох операцій виконання операції з високою точністю і округлення назад до меншої точності гарантуватиме правильну відповідь, що означає, що відповідь буде гарантовано однаковою для всіх систем. Чи отримаєте ви додаткову точність, не має значення, оскільки у вас є достатня точність, щоб гарантувати правильну відповідь в будь-якому випадку.

Операції, які повинні працювати за цією схемою: додавання, віднімання, множення, ділення, sqrt. Такі речі, як sin, exp тощо, не працюватимуть (результати зазвичай збігаються, але гарантії немає). "Коли подвійне округлення нешкідливо?" Довідка ACM (платний реєстр. Запит)

Сподіваюся, це допомагає!


2
Також проблема, що .NET 5, або 6, або 42, більше не може використовувати режим обчислення x87. У стандарті немає нічого, що цього вимагає.
Ерік Дж.

5

Як уже було сказано в інших відповідях: Так, це проблема в C # - навіть якщо ви залишаєтеся чистими Windows.

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

Як вимагає ОП - щодо ефективності:

System.Decimalпредставляє число з 1 бітом для знака та 96-бітним цілим числом та "шкалою" (представляє де десяткова крапка). Для всіх обчислень, які ви робите, він повинен працювати на цій структурі даних і не може використовувати жодних інструкцій з плаваючою комою, вбудованих у процесор.

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

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

Щоб зменшити хіт продуктивності, існує декілька стратегій - наприклад, QNumbers (див. Відповідь Джонатана Дікінсона. Чи відповідає математика з плаваючою комою в C #? Чи може це бути? ) Та / або кешування (наприклад, обчислення тригерів ...) і т.д.


1
Зверніть увагу, що BigIntegerце доступно лише у .Net 4.0.
svick

Я здогадуюсь, що хіт на продуктивність BigIntegerперевищує навіть показник, який потрапив у десятковий.
CodesInChaos

Кілька разів у відповідях тут згадується хіт на продуктивність використання Decimal(@Jonathan Dickinson - 'собака повільний') або BigInteger(коментар @CodeInChaos вище) - чи може хтось, будь ласка, дати трохи пояснення щодо цих хітів виконання та про те, чи / чому вони насправді є зупинками для забезпечення вирішення.
Barry Kaye

@Yahia - дякую за редагування - цікаве читання, однак, чи можете ви, будь-ласка, також дайте оцінку ефективності хіту використання не "float"? Ми говоримо на 10% повільніше або в 10 разів повільніше - я просто хочете отримати відчуття порядку, що мається на увазі.
Barry Kaye

це більше схоже на площу 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 #.


"компілювати в 32-розрядний .dll і потім використовувати ... AnyCpu". Я думаю, що це буде працювати лише під час роботи в 32-бітовій системі. У 64-бітовій системі лише програмне націлення x86зможе завантажити 32-бітний dll.
CodesInChaos

2

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

Стратегія, яку я прийняв би, по суті така:

  • Використовуйте повільніший (якщо необхідно; якщо швидший шлях, чудовий!), Але передбачуваний метод для отримання відтворюваних результатів
  • Використовуйте подвійний для всього іншого (наприклад, надання)

Короткий термін цього: потрібно знайти баланс. Якщо ви витрачаєте 30мм на візуалізацію (~ 33 кадрів в секунду) і лише 1 мс робите виявлення зіткнень (або вставляєте якусь іншу високочутливу операцію) - навіть якщо ви потроїть час, необхідний для виконання критичної арифметики, вплив, який він робить на ваш кадр, є ви падаєте з 33,3 кадрів в секунду до 30,3 кадрів в секунду.

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


1

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


завжди прив'язуючи кожну операцію до 30 або 31 біт - саме це floatробить тип даних на машинах x86 - однак це призведе до дещо інших результатів від машин, які проводять усі свої обчислення, використовуючи лише 32 біти, і ці невеликі зміни поширюватимуться з часом. Звідси і питання.
BlueRaja - Danny Pflughoeft

Якщо "N біт точності" означає, що будь-який обчислення точний тому, що багато біт, а машина A точна до 32 біт, тоді як машина B точна до 48 біт, то перші 32 біти будь-якого обчислення на обох машинах повинні бути однаковими. Не вдалося б обрізати до 32 біт або менше після кожної операції, щоб обидва машини точно синхронізувались? Якщо ні, то який приклад?
Захист свідків ID 44583292

-3

Ваше запитання у досить складних та технічних матеріалах O_o. Однак у мене може бути ідея.

Ви впевнені, знаєте, що процесор вносить певні корективи після будь-яких плаваючих операцій. А процесор пропонує декілька різних інструкцій, які роблять різні операції округлення.

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

Помилки, допущені коригуванням округлення, зростатимуть з кожною подальшою інструкцією.

Як приклад можна сказати, що на рівні складання: a * b * c не еквівалентно a * c * b.

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

Однак, щоб відповісти на ваше запитання: на C або C ++ ви можете вирішити свою проблему, оскільки у вас є деякий контроль над машинним кодом, сформованим вашим компілятором, однак у .NET у вас немає. Тож поки ваш машинний код може бути різним, ви ніколи не будете впевнені в точному результаті.

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

Я сподіваюся, що я був досить зрозумілий, я не досконалий англійською (... зовсім: s)


9
Уявіть стрілець P2P. Ви стріляєте в хлопця, б'єте його, і він помирає, але це дуже близько, ви майже не пропустили. На ПК іншого хлопця використовується дещо інші розрахунки, і він обчислює, що ви сумуєте. Ви бачите проблему зараз? У цьому випадку збільшення розміру регістрів не допоможе (принаймні, не повністю). Використання точно такого ж розрахунку на кожному комп’ютері буде.
svick

5
У цьому сценарії один зазвичай не піклується про те , як близько результат фактичного результату ( до тих пір , як це розумно), але важливо те , що це точно те ж саме для всіх користувачів.
CodesInChaos

1
Ви маєте рацію, я не хотів про подібний сценарій. Однак я згоден з @CodeInChaos щодо цього. Я не вважав, що дуже розумно приймати важливе рішення двічі. Це більше питання архітектури програмного забезпечення. Одна програма, заявка стрільця для зразка, повинна зробити розрахунок і надіслати результат іншим. Ви ніколи не матимете помилок таким чином. У вас є удар чи ні, але лише один приймає рішення. Як скажіть @driushkin
AxFab

2
@Aesgar: Так, саме так працює більшість стрільців; що "повноваження" називається сервером, а загальну архітектуру ми називаємо архітектурою "клієнт / сервер". Однак існує й інший вид архітектури: одноранговий. У P2P немає сервера; швидше, всі клієнти повинні перевірити всі дії один з одним, перш ніж щось трапиться. Це збільшує відставання, що робить його неприйнятним для стрільців, але значно зменшує мережевий трафік, що робить його ідеальним для ігор, де невеликий відставання (~ 250 мс) є прийнятним, але синхронізація всього ігрового стану не є. А саме ігри RTS, такі як C&C та Starcraft, використовують P2P.
BlueRaja - Danny Pflughoeft

5
У грі p2p у вас немає надійної машини, на яку можна покластися. Якщо ви дозволите одній станції вирішити, ударила його куля чи ні, ви відкриєте можливість обману клієнта. Крім того, посилання навіть не можуть обробляти кількість даних, яка іноді призводить до результатів - ігри працюють, надсилаючи замовлення, а не результати. Я граю в ігри РТС, і багато разів бачив стільки сміття, що летить навколо, немає ніякого способу його переслати над звичайними побутовими посиланнями.
Лорен Печтел
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.