C #: Come posso fare matematica semplice, con arrotondamenti, su numeri interi?

StackOverflow https://stackoverflow.com/questions/299094

  •  08-07-2019
  •  | 
  •  

Domanda

Voglio il risultato di un'equazione arrotondata al numero intero più vicino. per esempio.

137 * (3/4) = 103

Considera il seguente codice errato.

int width1 = 4;
int height1 = 3;

int width2 = 137;
int height2 = width2 * (height1 / width1);

Qual è il modo corretto di eseguire "intero"? matematica in C #?

Devo davvero fare:

int height2 = (int)Math.Round(
      (float)width2 * ((float)height1 / (float)width1)
   );
È stato utile?

Soluzione

Come detto sopra, dovresti fare la moltiplicazione prima della divisione. Ad ogni modo potresti scrivere la tua espressione con il cast solo del divisore:

int height2 = (int)Math.Round(width2 * (height1 / (float)width1));

Altri suggerimenti

int height2 = (width2 * height1) / width1;

Esegui prima la moltiplicazione (a condizione che non abbiano probabilità di traboccare) e poi la divisione.

3/4 == 0 nella matematica dei numeri interi (la matematica dei numeri interi non arrotonda alla divisione, si tronca).

Se hai davvero bisogno di arrotondare, devi lavorare in virgola fissa, in virgola mobile o giocare con il modulo.

Penso che questo sia il modo più elegante per farlo:

Math.round(myinteger * 0.75);

L'uso di 0.75 invece di 3/4 lo implicitamente lancerà su un double / float e perché non usare le funzioni predefinite fornite per questo?

Riparerò il codice errato aggiungendo zero righe di codice:

float width1 = 4;
float height1 = 3;

float width2 = 137;
float height2 = width2 * (height1 / width1);

Dovresti usare i float per le variabili che possono eventualmente contenere decimali. Ciò include le altezze calcolate dai rapporti. Puoi sempre lanciare int in seguito se questo è un problema.

Non eseguire mai il cast su float, ha ancora meno precisione di un numero intero a 32 bit. Se hai intenzione di utilizzare il virgola mobile, usa sempre double anziché float.

Mi sono appena imbattuto in questa domanda. La risposta di Aaron mi sembra quasi giusta. Ma sono molto sicuro che devi specificare il punto centrale se il tuo problema è un "mondo reale" problema. Quindi la mia risposta, basata sul codice di Aaron, è

int height2 = (int)Math.Round(width2 * (height1 / (float)width1),MidpointRounding.AwayFromZero);

Per vedere la differenza esegui quel codice nella console

    Console.WriteLine((int)Math.Round(0.5));
    Console.WriteLine((int)Math.Round(1.5));
    Console.WriteLine((int)Math.Round(2.5));
    Console.WriteLine((int)Math.Round(3.5));
    Console.WriteLine((int)Math.Round(0.5, MidpointRounding.AwayFromZero));
    Console.WriteLine((int)Math.Round(1.5, MidpointRounding.AwayFromZero));
    Console.WriteLine((int)Math.Round(2.5, MidpointRounding.AwayFromZero));
    Console.WriteLine((int)Math.Round(3.5, MidpointRounding.AwayFromZero));
    Console.ReadLine();

Per i dettagli puoi consultare questo articolo.

Sei anni (arrotondati) dopo, ecco il mio contributo - un piccolo trucco che ho imparato molto tempo fa e sono sorpreso che nessun altro abbia menzionato qui.

L'idea è di arrotondare aggiungendo metà del divisore al numeratore prima di eseguire la divisione.

    int height2 = (width2 * height1 + width1 / 2) / width1;

In realtà non consiglierei necessariamente di farlo in casi, come gli OP, in cui il divisore è una variabile. Invece potrebbe essere meglio usare Math.Round (), poiché è molto più facile da capire.

Ma nei casi in cui il divisore è una costante uso questo trucco. Quindi invece di

    int height2 = width2 * height1 / 4;  // No rounding

Userei

    int height2 = (width2 * height1 + 2) / 4;

Ecco un esempio più tipico

  private static Size ResizeWithPercentage(Size oldSize, int resizePercentage)
  {
     return new Size((oldSize.Width * resizePercentage + 50) / 100, 
                     (oldSize.Height * resizePercentage + 50) / 100);
  }

Un'altra possibilità è combinare questo trucco con l'idea menzionata da dongilmore e supercat che invece di avere una divisione per due specificata o implicita, è possibile moltiplicare sia il numeratore che il denominatore per due.

    int height2 = (width2 * height1 * 2 + width1) / (width1 * 2);

Ciò fornisce risposte migliori nei casi in cui il divisore è, o può essere, un numero dispari.

L'elaborato messaggio di Jeffery, poiché in genere hai maggiori possibilità di troncare i decimali necessari rispetto a quelli che hai traboccando un numero intero a 32 bit (e poiché la moltiplicazione e la divisione sono commutative), dovresti generalmente fare moltiplicazioni prima della divisione.

Le conversioni di Convert sono sempre arrotondate, quindi:

int height2 = Convert.ToInt32(width2 * height1 / (double)width1);
int height2 = ((width2 * height1 * 10) + 5) / (width1 * 10);

Prima di qualsiasi divisione di numeri interi, verificare che il divisore non sia zero. Si noti inoltre che ciò presuppone un quoziente positivo. Se negativo, il roundoff deve essere -5, non +5.

Un approccio ragionevolmente efficace se la divisione finale sarà per un numero pari e il risultato sarà sempre positivo è quello di dividere per metà quel valore, aggiungere uno e dividere per due. Se il risultato può essere negativo, se possibile aggiungere un importo che renderà tutto positivo, eseguire il calcolo e sottrarre successivamente un importo corrispondente.

Se la divisione finale sarà per un numero dispari, moltiplicare sia il numeratore che il denominatore per 2, quindi procedere come sopra. Ad esempio, per calcolare un * 5/7, arrotondato, calcolare (a * 10 + 1) > > 1 . L'unica cosa a cui prestare attenzione è che potrebbe essere necessario estendere i valori a un tipo numerico più grande per evitare overflow o, se ciò non è possibile, suddividere la divisione in pezzi. Ad esempio, per calcolare un * 14/15, è possibile calcolare ((a * 4/3 * 7) / 5 + 1) / 2. Tale calcolo potrebbe comunque traboccare se a è troppo grande, ma l'intervallo consentito sarà tre volte maggiore come se fosse valutato senza fare la divisione per 3 prima dell'altra divisione. Si noti che suddividere l'operazione renderà l'arrotondamento leggermente meno preciso, ma comunque abbastanza vicino per molti scopi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top