2013-07-07 9 views
6

AGGIORNAMENTO: Grazie Kevin, mi significava < = nel titolo, non <.Per doppia f, g galleggiante, Come trovare più grande che int, in modo tale che i * g <= f

È corretto? E 'il migliore che sono riuscito, dopo una quantità imbarazzante di tempo sul problema:

public int candidate_answer(double f, float g) { 
    int test = (int)Math.floor(f/g); 
    if ((test + 1) * g <= f) { 
     test++; 
    } 
    return test; 
} 

Background:

L'applicazione è un gioco semplice che ho preso sulla proprietà per un programmatore dal precedente . Curiosamente, ha scelto di mescolare i float e di raddoppiare in modo arbitrario, sia nelle variabili membro che in quella argomento, quindi c'è un sacco di casting implicito ed esplicito non necessario che va su e giù.

Le coordinate del giocatore sono al doppio x, y (si presume che il giocatore sia un punto). C'è un float TILE_SIZE, e il mondo è un certo numero di righe e colonne vale la pena di piastrelle, oltre ad alcune generiche manipolazioni fuori dai limiti. Supponendo che le coordinate (x, y) siano vincolate, sto cercando di capire quale tessera l'utente si trova, in base a x (per ottenere una colonna) o a y (per ottenere una riga). Questo è come, programmatore 101.

WLOG, All'inizio stavo solo facendo col = (int)(x/TILE_SIZE). Ma come ho scoperto, ad esempio, .5/.1f < 5, e quindi (int)(.5/.1f) == 4, la risposta sbagliata. Ciò ha portato alla suddetta affermazione e formulazione del problema.

Quindi ho scoperto che, ad esempio, (int)(-9.999999747378752E-5/.1f) == 0, che mi ha portato a chiamare prima Math.floor.

Ma ora non sono sicuro di quali altri bug si nascondano in questo approccio o quale sia l'approccio migliore.

(Potrebbe non sembrare un grosso problema se l'utente è sul punto di trovarsi in una fila o nell'altra, e accidentalmente si arriva a quello sbagliato, ma il vero problema è nel codice, dove vedi le modifiche ai segni inattese (+, -, 0). Ad esempio, parte del codice parte dal presupposto che se l'utente si trova sulla tessera in (r, c), allora il suo punto è effettivamente contenuto da quella geometria della piastrella. otteniamo cose come distanze negative quando ci si aspetta solo non-negativi, ecc., e fa sì che gli asseriti si attivino e mentre i loop si interrompano e così via.)

+0

'i * g Kevin

+0

<=, e aggiornato, grazie – jdowdell

risposta

3

Penso che dovresti provare a risolvere il problema di root (. 1f troppo lontano da .1), ovvero trasforma TILE_SIZE in un doppio, o usa float in modo coerente ovunque. In caso contrario si potrebbero avere problemi simili causati da errori di arrotondamento minimi in tutto il codice. La funzione candidate_answer è un trucco per aggirare un problema che non dovrebbe esistere in primo luogo.

P.S.

Per questo particolare problema, è possibile che si desideri trovare una formula più robusta, ovvero non sensibile agli errori di arrotondamento minimi in un'unica direzione. Prova a mettere il giocatore al centro del suo campo invece che a bordo delle mattonelle in cui si può facilmente cadere in errori di arrotondamento:

col = (int)((x + MINIMAL_STEP_SIZE/2.0)/TILE_SIZE) 

(Idealmente, la parte MINIMAL_STEP_SIZE/2 sarebbe già parte di x invece di essere aggiunto qui)

+1

Sono d'accordo con la risoluzione del problema di root, ma penso che un modo migliore per farlo sarebbe passare a una rappresentazione integrale a virgola fissa, con TILE_SIZE 256 e tutte le coordinate con numeri interi a 32 bit, si dispone di un sacco di risoluzione all'interno di ciascuna tessera e un'area della mappa di 16 milioni di tessere. – NovaDenizen

+0

Concordato, ma potrebbe essere un refactoring molto più grande, e il punto fisso ha il suo insieme di problemi (ad es. preoccuparsi di traboccare in moltiplicazioni, potrebbe essere più difficile eseguire il debug) .... –

1

L'uso del doppio sembra essere la strada da percorrere. Tuttavia, questo non risolve il problema di root: l'utilizzo dell'aritmetica in virgola mobile è sempre associato agli errori di arrotondamento.

Quando si usa aritmetica in virgola mobile, è sempre bene a lavorare con un Epsilon (e) di accuratezza:

Se si desidera controllare se x è uguale ay (dove una variabile è un tipo in virgola mobile) , utilizzare il seguente schema:

if(x-y < e){ 
    ... 
} 

Epsilon deve essere definita (a livello globale o al vostro parere per un determinato dominio) in base alle tue esigenze. Per esempio:

e = 0.00001d; 
+0

Lanciare un epsilon e ora avete due problemi invece di uno: qual è l'epsilon universale giusto? E tu non mostri come questo possa essere d'aiuto per l'attuale problema originale. Se si esegue questa operazione, è possibile utilizzare solo numeri a virgola fissa, poiché in pratica elimina l'aspetto a virgola mobile dei numeri in virgola mobile. Considera 0,000001 e 0,000002: la differenza è inferiore al tuo epsilon, tuttavia un numero è due volte l'altro. Penso che qui il problema di root sia posizionare un player di dimensioni puntuali proprio al limite del proprio campo invece che al centro di esso, il che rende tutto incline agli errori di arrotondamento. –

0

Se f e g sono entrambi positivi (che sembrano essere), il metodo può essere ridotto a un unico, semplice calcolo:

public static int candidate_answer(double f, float g) { 
    return (int)(g/f); 
} 

Questo è corretto, perché se

i*f <= g 

quindi dividendo entrambi i lati da f cede

i <= g/f 

La trasmissione a int fornisce automaticamente "il più grande int minore o uguale al risultato" perché quando si esegue il cast di tutte le parti non interi vengono scartate, è come eseguire una chiamata floor().


Se f o g sono autorizzati a essere negativo, questo approccio potrebbe funzionare, ma trattare con il segno dovrebbe essere aggiunto (non molto codice, ma dubito che si riveli necessaria data loro modo la domanda è

+0

Hai letto la domanda? "All'inizio facevo solo col = (int) (x/TILE_SIZE). Ma come ho scoperto, ad esempio, .5/.1f <5, e quindi (int) (. 5/.1f) == 4, il risposta sbagliata, che ha portato alla suddetta affermazione e formulazione del problema ". –

Problemi correlati