La matematica in virgola mobile è coerente in C #? Può essere?


155

No, questa non è un'altra domanda "Why is (1 / 3.0) * 3! = 1" .

Ultimamente ho letto parecchi punti in virgola mobile; in particolare, come lo stesso calcolo potrebbe dare risultati diversi su architetture o impostazioni di ottimizzazione diverse.

Questo è un problema per i videogiochi che memorizzano replay o sono collegati in rete peer-to-peer (al contrario del server-client), che si affidano a tutti i client che generano esattamente gli stessi risultati ogni volta che eseguono il programma - una piccola discrepanza in uno il calcolo in virgola mobile può portare a uno stato di gioco drasticamente diverso su macchine diverse (o persino sulla stessa macchina! )

Ciò accade anche tra i processori che "seguono" IEEE-754 , principalmente perché alcuni processori (vale a dire x86) usano una doppia precisione estesa . Cioè, usano i registri a 80 bit per eseguire tutti i calcoli, quindi si troncano a 64 o 32 bit, portando a risultati di arrotondamento diversi rispetto alle macchine che usano 64 o 32 bit per i calcoli.

Ho visto diverse soluzioni a questo problema online, ma tutte per C ++, non C #:

  • Disabilitare la doppia modalità di precisione estesa (in modo che tutti i doublecalcoli utilizzino IEEE-754 a 64 bit) utilizzando _controlfp_s(Windows), _FPU_SETCW(Linux?) O fpsetprec(BSD).
  • Esegui sempre lo stesso compilatore con le stesse impostazioni di ottimizzazione e richiedi a tutti gli utenti di avere la stessa architettura della CPU (nessuna riproduzione multipiattaforma). Poiché il mio "compilatore" è in realtà il JIT, che può ottimizzare in modo diverso ogni volta che il programma viene eseguito , non penso che sia possibile.
  • Usa l'aritmetica in virgola fissa, ed evita floate del doubletutto. decimalfunzionerebbe a questo scopo, ma sarebbe molto più lento e nessuna delle System.Mathfunzioni di libreria lo supporta.

Quindi, questo è anche un problema in C #? Cosa succede se intendo supportare solo Windows (non Mono)?

In tal caso, esiste un modo per forzare l'esecuzione del mio programma alla normale doppia precisione?

In caso contrario, ci sono librerie che potrebbero aiutare a mantenere coerenti i calcoli in virgola mobile?


Ho visto questa domanda , ma ogni risposta o ripete il problema senza soluzione, o dice "ignoralo", che non è un'opzione. Ho fatto una domanda simile su Gamedev , ma (a causa del pubblico) la maggior parte delle risposte sembrano orientate verso il C ++.
BlueRaja - Danny Pflughoeft,

1
non è una risposta, ma sono sicuro che nella maggior parte dei domini potresti progettare il tuo sistema in modo tale che tutto lo stato condiviso sia deterministico, e non vi è alcun degrado significativo delle prestazioni a causa di ciò
driushkin

1
@Pietro conosci qualche emulazione in virgola mobile veloce per .net?
CodesInChaos,

1
Java soffre di questo problema?
Josh

3
@Josh: Java ha la strictfpparola chiave, che impone che tutti i calcoli vengano eseguiti nella dimensione dichiarata ( floato double) anziché in una dimensione estesa. Tuttavia, Java ha ancora molti problemi con il supporto IEE-754. Pochissimi (molto, molto) pochi linguaggi di programmazione supportano bene IEE-754.
porges

Risposte:


52

Non conosco alcun modo per rendere deterministici i normali punti fluttuanti in .net. A JITter è consentito creare codice che si comporta diversamente su piattaforme diverse (o tra versioni diverse di .net). Quindi floatnon è possibile usare normali s in codice .net deterministico.

Le soluzioni alternative che ho considerato:

  1. Implementare FixedPoint32 in C #. Anche se questo non è troppo difficile (ho un'implementazione quasi finita), l'intervallo molto piccolo di valori lo rende fastidioso da usare. Devi stare attento in ogni momento in modo da non traboccare, né perdere troppa precisione. Alla fine l'ho trovato non più semplice dell'utilizzo diretto di numeri interi.
  2. Implementare FixedPoint64 in C #. Ho trovato questo piuttosto difficile da fare. Per alcune operazioni sarebbero utili interi intermedi di 128 bit. Ma .net non offre questo tipo.
  3. Implementare un punto mobile personalizzato a 32 bit. La mancanza di un intrinseco BitScanReverse provoca alcuni fastidi durante l'implementazione. Ma attualmente penso che questo sia il percorso più promettente.
  4. Utilizzare il codice nativo per le operazioni matematiche. Supporta l'overhead di una chiamata delegata su ogni operazione matematica.

Ho appena iniziato un'implementazione software di matematica a virgola mobile a 32 bit. Può fare circa 70 milioni di aggiunte / moltiplicazioni al secondo sul mio i3 a 2,66 GHz. https://github.com/CodesInChaos/SoftFloat . Ovviamente è ancora molto incompleto e difettoso.


2
c'è un numero intero di dimensioni "illimitate" disponibile BigInteger anche se non tanto veloce quanto nativo int o lungo è lì, quindi .NET offre un tale tipo (creato per F # credo ma può essere utilizzato in C #)
Rune FS

Un'altra opzione è il wrapper GNU MP per .NET . È un involucro attorno alla GNU Multiple Precision Library che supporta numeri interi di precisione "inifinite", razionali (frazioni) e numeri in virgola mobile.
Cole Johnson,

2
Se hai intenzione di fare uno di questi, potresti anche provare decimalprima, poiché è molto più semplice. Solo se è troppo lento per il compito da svolgere vale la pena pensare ad altri approcci.
Roman Starkov,

Ho imparato un caso speciale in cui i punti fluttuanti sono deterministici. La spiegazione che ho ottenuto è: per la moltiplicazione / divisione, se uno dei numeri FP è la potenza di due numeri (2 ^ x), significativo / mantissa non cambierà durante il calcolo. Solo l'esponente cambierà (il punto si sposterà). Quindi l'arrotondamento non accadrà mai. Il risultato sarà deterministico.
zigzag

Esempio: un numero come 2 ^ 32 è rappresentato come (esponente: 32, mantissa: 1). Se lo moltiplichiamo con un altro float (exp, man), il risultato è (exp + 32, man * 1). Per divisione, il risultato è (expo - 32, man * 1). Moltiplicare la mantissa per 1 non cambia la mantissa, quindi non importa quanti bit ha.
zigzag,

28

La specifica C # (§4.1.6 Tipi in virgola mobile) consente specificamente di eseguire calcoli in virgola mobile utilizzando una precisione superiore a quella del risultato. Quindi, no, non penso che tu possa rendere quei calcoli deterministici direttamente in .Net. Altri hanno suggerito varie soluzioni alternative, in modo da poterle provare.


9
Mi sono appena reso conto che la specifica C # non ha molta importanza se si distribuiscono assiemi compilati. Importa solo se si desidera la compatibilità dei sorgenti. Ciò che conta davvero è la specifica CLR. Ma sono abbastanza sicuro che le garanzie siano altrettanto deboli delle garanzie C #.
Codici InCos

Non eseguire il casting doubleogni volta dopo un'operazione eliminando i bit indesiderati, ottenendo risultati coerenti?
IllidanS4 vuole che Monica ritorni il

2
@ IllidanS4 Non credo che garantirebbe risultati coerenti.
svick,

14

La pagina seguente può essere utile nel caso in cui sia necessaria la portabilità assoluta di tali operazioni. Discute il software per testare le implementazioni dello standard IEEE 754, incluso il software per emulare operazioni in virgola mobile. La maggior parte delle informazioni è probabilmente specifica per C o C ++, tuttavia.

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

Una nota sul punto fisso

I numeri binari a virgola fissa possono anche funzionare bene come sostituto del virgola mobile, come risulta dalle quattro operazioni aritmetiche di base:

  • Aggiunta e sottrazione sono banali. Funzionano allo stesso modo degli interi. Aggiungi o sottrai!
  • Per moltiplicare due numeri a virgola fissa, moltiplicare i due numeri quindi spostare a destra il numero definito di bit frazionari.
  • Per dividere due numeri in virgola fissa, spostare il dividendo a sinistra del numero definito di bit frazionari, quindi dividere per il divisore.
  • Il capitolo quattro di questo documento contiene ulteriori indicazioni sull'implementazione di numeri binari a virgola fissa.

I numeri binari a virgola fissa possono essere implementati su qualsiasi tipo di dati intero come int, long e BigInteger e i tipi non conformi CLS uint e ulong.

Come suggerito in un'altra risposta, è possibile utilizzare le tabelle di ricerca, in cui ogni elemento nella tabella è un numero binario a punto fisso, per aiutare a implementare funzioni complesse come seno, coseno, radice quadrata e così via. Se la tabella di ricerca è meno granulare del numero in virgola fissa, si consiglia di arrotondare l'input aggiungendo una metà della granularità della tabella di ricerca all'input:

// 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
Dovresti caricarlo su un sito di progetto di codice open source, come sourceforge o github. Questo rende più facile da trovare, più facile contribuire, più facile da inserire nel tuo curriculum ecc. Inoltre, alcuni suggerimenti per il codice sorgente (sentiti libero di ignorare): usa constinvece che staticper le costanti, in modo che il compilatore possa ottimizzarle; preferire le funzioni membro a funzioni statiche (in modo da poter chiamare, ad es. myDouble.LeadingZeros()anziché IntDouble.LeadingZeros(myDouble)); cerca di evitare i nomi delle variabili a lettera singola ( MultiplyAnyLengthad esempio, ha 9, rendendolo molto difficile da seguire)
BlueRaja - Danny Pflughoeft

Prestare attenzione all'uso di uncheckedtipi non conformi a CLS come ulong, uintecc. Per scopi di velocità - poiché sono usati così raramente, il JIT non li ottimizza in modo aggressivo, quindi il loro utilizzo può effettivamente essere più lento rispetto all'utilizzo di tipi normali come longe int. Inoltre, C # ha un sovraccarico dell'operatore , di cui questo progetto trarrebbe grande beneficio. Infine, ci sono test unitari associati? Oltre a queste piccole cose, fantastico lavoro Peter, questo è ridicolmente impressionante!
BlueRaja - Danny Pflughoeft il

Grazie per i commenti Eseguo test unitari sul codice. Sono piuttosto estesi, tuttavia, troppo estesi per essere rilasciati per ora. Scrivo anche routine di supporto per unit test per rendere più semplice la scrittura di più test. Non uso operatori sovraccaricati per ora perché ho intenzione di tradurre il codice in Java quando avrò finito.
Peter O.

2
La cosa divertente è che quando ho pubblicato sul tuo blog non ho notato che il blog era tuo. Avevo appena deciso di provare google + e nella sua scintilla in C # mi ha suggerito di entrare nel blog. Quindi ho pensato "Che straordinaria coincidenza per noi due iniziare a scrivere una cosa simile allo stesso tempo". Ma ovviamente abbiamo avuto lo stesso grilletto :)
CodesInChaos

1
Perché preoccuparsi di portarlo su Java? Java ha già garantito la matematica deterministica in virgola mobile tramite strictfp.
Antimonio

9

È un problema per C #?

Sì. Architetture diverse sono le ultime preoccupazioni, framerati diversi, ecc. Possono portare a deviazioni dovute a imprecisioni nelle rappresentazioni float - anche se hanno le stesse imprecisioni (ad esempio la stessa architettura, tranne una GPU più lenta su una macchina).

Posso usare System.Decimal?

Non c'è motivo per cui non puoi, tuttavia è un cane lento.

C'è un modo per forzare l'esecuzione del mio programma in doppia precisione?

Sì. Ospita tu stesso il runtime CLR ; e compilare tutte le chiamate / flag di nessecary (che modificano il comportamento dell'aritmetica in virgola mobile) nell'applicazione C ++ prima di chiamare CorBindToRuntimeEx.

Esistono librerie che potrebbero aiutare a mantenere coerenti i calcoli in virgola mobile?

Non che io sappia.

C'è un altro modo per risolvere questo?

Ho già affrontato questo problema in precedenza, l'idea è di utilizzare QNumbers . Sono una forma di reali a virgola fissa; ma non punto fisso in base-10 (decimale) - piuttosto base-2 (binario); per questo i primitivi matematici su di essi (add, sub, mul, div) sono molto più veloci dei punti fissi ingenui base-10; specialmente se nè lo stesso per entrambi i valori (che nel tuo caso sarebbe). Inoltre, poiché sono integrali, hanno risultati ben definiti su ogni piattaforma.

Tieni presente che il framerate può ancora influire su questi, ma non è così male e può essere facilmente corretto utilizzando i punti di sincronizzazione.

Posso usare più funzioni matematiche con QNumbers?

Sì, andata e ritorno di un decimale per farlo. Inoltre, dovresti davvero utilizzare le tabelle di ricerca per le funzioni trig (sin, cos); poiché possono davvero dare risultati diversi su piattaforme diverse e se li codifichi correttamente possono usare direttamente QNumbers.


3
Non sono sicuro di cosa stai parlando con problemi di framerate. Chiaramente vorresti avere una frequenza di aggiornamento fissa (vedi ad esempio qui ) - che sia o meno lo stesso del display-framerate è irrilevante. Finché le imprecisioni sono le stesse su tutte le macchine, siamo a posto. Non capisco affatto la tua terza risposta.
BlueRaja - Danny Pflughoeft

@BlueRaja: la risposta "Esiste un modo per forzare l'esecuzione del mio programma in doppia precisione?" equivarrebbe a reimplementare l'intero Common Language Runtime, il che sarebbe estremamente complicato, oppure utilizzare le chiamate native a una DLL C ++ dall'applicazione C #, come suggerito nella risposta dell'utente shelleybutterfly. Pensa a "QNumbers" semplicemente come numeri binari a virgola fissa, come suggerito nella mia risposta (non avevo visto fino ad ora numeri binari a virgola fissa chiamati "QNumbers").
Peter O.

@Pieter O. Non è necessario reimplementare il runtime. Il server su cui lavoro nella mia azienda ospita il runtime CLR come applicazione C ++ nativa (così come SQL Server). Ti consiglio di Google CorBindToRuntimeEx.
Jonathan Dickinson,

@BlueRaja dipende dal gioco in questione. L'applicazione di passaggi di framerate fissi a tutti i giochi non è un'opzione praticabile, poiché l'algoritmo AOE introduce la latenza artificiale; che è inaccettabile, ad esempio in un FPS.
Jonathan Dickinson,

1
@ Jonathan: questo è solo un problema nei giochi peer-to-peer che inviano solo l'ingresso - per questi, si hanno per avere un aggiornamento a tasso fisso. La maggior parte degli FPS non funziona in questo modo, ma i pochi che hanno necessariamente una frequenza di aggiornamento fissa. Vedere questa domanda .
BlueRaja - Danny Pflughoeft il

6

Secondo questo post di blog MSDN leggermente vecchio, JIT non utilizzerà SSE / SSE2 per il virgola mobile, è tutto x87. Per questo motivo, come hai detto, devi preoccuparti di modalità e flag, e in C # questo non è possibile controllare. Pertanto, l'utilizzo delle normali operazioni in virgola mobile non garantirà lo stesso risultato esatto su ogni macchina per il proprio programma.

Per ottenere una riproducibilità precisa della doppia precisione, dovrai eseguire un'emulazione software a virgola mobile (o punto fisso). Non conosco le librerie C # per farlo.

A seconda delle operazioni necessarie, potresti riuscire a cavartela con una sola precisione. Ecco l'idea:

  • archivia tutti i valori che ti interessano in un'unica precisione
  • per eseguire un'operazione:
    • espandere gli input con doppia precisione
    • eseguire operazioni in doppia precisione
    • converti il ​​risultato in precisione singola

Il grosso problema con x87 è che i calcoli potrebbero essere eseguiti con precisione a 53 o 64 bit a seconda del flag di precisione e se il registro è stato versato in memoria. Ma per molte operazioni, l'esecuzione dell'operazione in alta precisione e l'arrotondamento alla precisione inferiore garantiranno la risposta corretta, il che implica che la risposta sarà garantita uguale su tutti i sistemi. Non importa se ottieni la precisione in più, dato che hai abbastanza precisione per garantire la risposta giusta in entrambi i casi.

Operazioni che dovrebbero funzionare in questo schema: addizione, sottrazione, moltiplicazione, divisione, sqrt. Cose come sin, exp, ecc. Non funzioneranno (i risultati di solito corrispondono ma non c'è garanzia). "Quando il doppio arrotondamento è innocuo?" Riferimenti ACM (req. Reg. Pagati)

Spero che questo ti aiuti!


2
È anche un problema che .NET 5, o 6, o 42, potrebbero non utilizzare più la modalità di calcolo x87. Non c'è nulla nello standard che lo richiede.
Eric J.

5

Come già affermato da altre risposte: Sì, questo è un problema in C #, anche quando si utilizza Windows puro.

Per quanto riguarda una soluzione: è possibile ridurre (e con un certo sforzo / hit hit) evitare completamente il problema se si utilizza la BigIntegerclasse integrata e si ridimensionano tutti i calcoli con una precisione definita utilizzando un denominatore comune per qualsiasi calcolo / memorizzazione di tali numeri.

Come richiesto dall'OP - per quanto riguarda le prestazioni:

System.Decimalrappresenta il numero con 1 bit per un segno e Integer a 96 bit e una "scala" (che rappresenta il punto decimale). Per tutti i calcoli effettuati, deve operare su questa struttura di dati e non può utilizzare alcuna istruzione in virgola mobile integrata nella CPU.

La BigInteger"soluzione" fa qualcosa di simile - solo che puoi definire quante cifre hai bisogno / desideri ... forse vuoi solo 80 bit o 240 bit di precisione.

La lentezza deriva sempre dal dover simulare tutte le operazioni su questo numero tramite istruzioni di solo numero intero senza utilizzare le istruzioni integrate CPU / FPU che a loro volta portano a molte più istruzioni per operazione matematica.

Per ridurre il calo delle prestazioni ci sono diverse strategie - come QNumbers (vedi risposta di Jonathan Dickinson - La matematica in virgola mobile è coerente in C #? Può essere? ) E / o memorizzazione nella cache (ad esempio calcoli di trigmi ...) ecc.


1
Si noti che BigIntegerè disponibile solo in .Net 4.0.
svick,

La mia ipotesi è che il colpo di prestazione BigIntegersuperi persino quello colpito da Decimale.
CodesInChaos,

Un paio di volte nelle risposte qui c'è un riferimento al colpo di performance dell'uso Decimal(@Jonathan Dickinson - 'dog slow') o BigInteger(commento di @CodeInChaos sopra) - qualcuno può fornire una piccola spiegazione su questi successi e se / perché sono davvero degli ostacoli per fornire una soluzione.
Barry Kaye,

@Yahia - grazie per la modifica - lettura interessante, tuttavia, potresti per favore anche dare una stima approssimativa del successo delle prestazioni del non utilizzo di 'float' se stiamo parlando del 10% più lentamente o 10 volte più lentamente - Io solo vuoi avere un'idea dell'ordine di grandezza implicito.
Barry Kaye,

è più probabile nell'area di 1: 5 che "solo il 10%"
Yahia

2

Bene, ecco il mio primo tentativo su come fare questo :

  1. Creare un progetto ATL.dll che contenga un oggetto semplice da utilizzare per le operazioni critiche in virgola mobile. assicurati di compilarlo con flag che disabilitano l'uso di qualsiasi hardware non xx87 per fare virgola mobile.
  2. Creare funzioni che chiamano operazioni in virgola mobile e restituiscono i risultati; iniziare in modo semplice e, se funziona per te, puoi sempre aumentare la complessità per soddisfare le tue esigenze prestazionali in un secondo momento, se necessario.
  3. Metti le chiamate control_fp intorno alla matematica attuale per assicurarti che sia fatto allo stesso modo su tutte le macchine.
  4. Fai riferimento alla tua nuova libreria e verifica per assicurarti che funzioni come previsto.

(Credo che tu possa semplicemente compilare un .dll a 32 bit e poi usarlo con x86 o AnyCpu [o probabilmente indirizzare solo x86 su un sistema a 64 bit; vedi commento sotto].)

Quindi, supponendo che funzioni, dovresti usare Mono immagino che dovresti essere in grado di replicare la libreria su altre piattaforme x86 in un modo simile (non ovviamente COM; anche se, forse, con wine? Un po 'fuori dalla mia area una volta ci andiamo però ...).

Supponendo che tu possa farlo funzionare, dovresti essere in grado di impostare funzioni personalizzate che possono eseguire più operazioni contemporaneamente per risolvere eventuali problemi di prestazioni e avrai matematica a virgola mobile che ti consente di ottenere risultati coerenti su piattaforme con un importo minimo di codice scritto in C ++ e lasciando il resto del codice in C #.


"compilare in una DLL a 32 bit e quindi utilizzare ... AnyCpu" Penso che funzionerà solo quando si esegue su un sistema a 32 bit. Su un sistema a 64 bit solo un targeting di programma x86sarà in grado di caricare la dll a 32 bit.
CodesInChaos,

2

Non sono uno sviluppatore di giochi, anche se ho molta esperienza con problemi computazionalmente difficili ... quindi, farò del mio meglio.

La strategia che vorrei adottare è essenzialmente questa:

  • Usa un metodo più lento (se necessario; se c'è un modo più veloce, fantastico!), Ma prevedibile per ottenere risultati riproducibili
  • Usa double per tutto il resto (ad es. Rendering)

Il breve di tutto questo è: devi trovare un equilibrio. Se stai spendendo 30ms di rendering (~ 33fps) e solo 1ms eseguendo il rilevamento delle collisioni (o inserisci qualche altra operazione altamente sensibile) - anche se tripli il tempo necessario per eseguire l'aritmetica critica, l'impatto che ha sul tuo framerate è si scende da 33,3 fps a 30,3 fps.

Ti suggerisco di profilare tutto, tenere conto di quanto tempo viene dedicato a ciascuno dei calcoli notevolmente costosi, quindi ripetere le misurazioni con 1 o più metodi per risolvere questo problema e vedere qual è l'impatto.


1

Il controllo dei collegamenti nelle altre risposte chiarisce che non avrai mai la garanzia che il punto mobile sia implementato "correttamente" o se riceverai sempre una certa precisione per un determinato calcolo, ma forse potresti fare uno sforzo migliore (1) troncando tutti i calcoli ad un minimo comune (ad esempio, se diverse implementazioni ti daranno da 32 a 80 bit di precisione, troncando sempre ogni operazione a 30 o 31 bit), (2) hanno una tabella di alcuni casi di test all'avvio (casi limite di aggiunta, sottrazione, moltiplicazione, divisione, sqrt, coseno, ecc.) e se l'implementazione calcola i valori corrispondenti alla tabella, non preoccuparsi di apportare modifiche.


troncando sempre ogni operazione a 30 o 31 bit - questo è esattamente ciò che fa il floattipo di dati su macchine x86 - tuttavia ciò causerà risultati leggermente diversi dalle macchine che eseguono tutti i loro calcoli usando solo 32 bit e queste piccole modifiche si propagheranno nel tempo. Da qui la domanda.
BlueRaja - Danny Pflughoeft

Se "N bit di precisione" indica che qualsiasi calcolo è accurato su quel numero di bit e la macchina A è accurata su 32 bit mentre la macchina B è accurata su 48 bit, i primi 32 bit di qualsiasi calcolo di entrambe le macchine dovrebbero essere identici. Troncare a 32 bit o meno dopo ogni operazione mantiene entrambe le macchine esattamente sincronizzate? In caso contrario, qual è un esempio?
ID protezione testimone 44583292

-3

La tua domanda in cose abbastanza difficili e tecniche O_o. Tuttavia potrei avere un'idea.

Sicuramente sai che la CPU esegue alcune regolazioni dopo qualsiasi operazione mobile. E la CPU offre diverse istruzioni che rendono diverse operazioni di arrotondamento.

Quindi, per un'espressione, il compilatore sceglierà una serie di istruzioni che ti condurranno a un risultato. Ma qualsiasi altro flusso di lavoro delle istruzioni, anche se intendono calcolare la stessa espressione, può fornire un altro risultato.

Gli "errori" commessi da un aggiustamento per arrotondamento cresceranno ad ogni ulteriore istruzione.

A titolo di esempio possiamo affermare che a livello di assieme: a * b * c non è equivalente a a * c * b.

Non ne sono del tutto sicuro, dovrai chiedere a qualcuno che conosce l'architettura della CPU molto più di me: p

Tuttavia per rispondere alla tua domanda: in C o C ++ puoi risolvere il tuo problema perché hai un certo controllo sul codice macchina generato dal tuo compilatore, tuttavia in .NET non ne hai. Quindi finché il codice della macchina può essere diverso, non sarai mai sicuro del risultato esatto.

Sono curioso di sapere in che modo questo può essere un problema perché la variazione sembra molto minima, ma se hai bisogno di un funzionamento davvero accurato l'unica soluzione a cui posso pensare sarà quella di aumentare le dimensioni dei tuoi registri mobili. Se possibile, usa la doppia precisione o anche il doppio lungo (non sei sicuro che sia possibile utilizzare la CLI).

Spero di essere stato abbastanza chiaro, non sono perfetto in inglese (... per niente: s)


9
Immagina uno sparatutto P2P. Spari a un ragazzo, lo colpisci e muore, ma è molto vicino, quasi ti sei perso. Sul PC dell'altro ragazzo usa calcoli leggermente diversi e calcola che ti manca. Vedi il problema adesso? In questo caso, aumentare le dimensioni dei registri non aiuterà (almeno non completamente). Utilizzando lo stesso calcolo esatto su ogni computer sarà.
svick,

5
In questo scenario di solito non ci si preoccupa di quanto il risultato sia vicino al risultato effettivo (purché sia ​​ragionevole), ma ciò che conta è che sia esattamente lo stesso per tutti gli utenti.
CodesInChaos,

1
Hai ragione, non ho pensato a questo tipo di scenario. Tuttavia, sono d'accordo con @CodeInChaos su questo. Non ho trovato molto intelligente prendere una decisione importante due volte. Questo è più un problema di architettura software. Un programma, la domanda del tiratore per esempio, dovrebbe effettuare il calcolo e inviare il risultato agli altri. Non avrai mai errori in questo modo. Hai un colpo o no, ma solo uno prende la decisione. Come dire @driushkin
AxFab il

2
@Aesgar: Sì, è così che funziona la maggior parte dei tiratori; tale "autorità" è chiamata server e chiamiamo l'architettura complessiva un'architettura "client / server". Tuttavia, esiste un altro tipo di architettura: peer-to-peer. In P2P, non esiste un server; piuttosto, tutti i client devono verificare tutte le azioni tra loro prima che accada qualcosa. Ciò aumenta il ritardo, rendendolo inaccettabile per i tiratori, ma diminuisce notevolmente il traffico di rete, rendendolo perfetto per i giochi in cui è accettabile un piccolo ritardo (~ 250 ms), ma la sincronizzazione dell'intero stato del gioco non lo è. Vale a dire, giochi RTS come C&C e Starcraft usano P2P.
BlueRaja - Danny Pflughoeft,

5
In un gioco p2p non hai una macchina affidabile su cui fare affidamento. Se si consente a una stazione di decidere se il suo proiettile ha colpito o meno si apre la possibilità di un client barare. Inoltre, i collegamenti non possono nemmeno gestire la quantità di dati che a volte risulta: i giochi funzionano inviando gli ordini anziché i risultati. Gioco ai giochi RTS e molte volte ho visto tanta spazzatura che volava in giro non c'è modo che potesse essere inviata tramite normali uplink domestici.
Loren Pechtel,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.