C# Perché la divisione è più lenta della moltiplicazione?

Nei linguaggi di programmazione e in particolare C# ci sono 4 operazioni aritmetiche che possono essere eseguite: addizione, sottrazione, moltiplicazione e divisione.

E da una prospettiva esterna, può sembrare che siano tutti simili in termini di prestazioni, ma risulta che uno di loro è molto più lento rispetto agli altri 3.

Quale è più lento potresti chiedere? Divisione.

Secondo questo documento HP:

La divisione in virgola mobile e la radice quadrata richiedono molto più tempo per essere calcolate rispetto all'addizione e alla moltiplicazione. Gli ultimi due vengono calcolati direttamente mentre i primi vengono solitamente calcolati con un algoritmo iterativo. L'approccio più comune consiste nell'utilizzare un'iterazione Newton-Raphson senza divisioni per ottenere un'approssimazione al reciproco del denominatore (divisione) o della radice quadrata reciproca, quindi moltiplicare per il numeratore (divisione) o l'argomento di input (radice quadrata).

Per verificare quanto affermato sopra ho deciso di eseguire un semplice test utilizzando il codice seguente:

        //Generate two random numbers
        var rand = new System.Random();
        float a = rand.Next();
        float b = rand.Next();

        Debug.Log("Number a: " + a + " Number b: " + b);

        System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();

        watch.Start();
        //Addition
        for (int i = 1; i < 1000000; i++)
        {
            float tmp = a + b;
        }
        watch.Stop();
        //Output
        Debug.Log("Addition took: " + watch.Elapsed.TotalSeconds.ToString("0.0000") + " seconds");

        watch.Reset();
        watch.Start();
        //Subtraction
        for (int i = 1; i < 1000000; i++)
        {
            float tmp = a - b;
        }
        watch.Stop();
        //Output
        Debug.Log("Subtraction took: " + watch.Elapsed.TotalSeconds.ToString("0.0000") + " seconds");

        watch.Reset();
        watch.Start();
        //Multiplication
        for (int i = 1; i < 1000000; i++)
        {
            float tmp = a * b;
        }
        watch.Stop();
        //Output
        Debug.Log("Multiplication took: " + watch.Elapsed.TotalSeconds.ToString("0.0000") + " seconds");

        watch.Reset();
        watch.Start();
        //Division
        for (int i = 1; i < 1000000; i++)
        {
            float tmp = a / b;
        }
        watch.Stop();
        //Division
        Debug.Log("Division took: " + watch.Elapsed.TotalSeconds.ToString("0.0000") + " seconds");

Fondamentalmente, ho eseguito un milione di addizioni, sottrazioni, moltiplicazioni e divisioni per i due numeri casuali e ho misurato il tempo impiegato da ciascuno di essi per elaborarli, il test è stato ripetuto 5 volte ed ecco il risultato:

  • L'aggiunta ha richiesto in media 0,0004 secondi
  • La sottrazione ha richiesto in media 0,0003 secondi
  • La moltiplicazione ha richiesto in media 0,0003 secondi
  • La divisione ha richiesto in media 0,0044 secondi

Il risultato ha mostrato che addizione, sottrazione e moltiplicazione sono simili in termini di prestazioni, ma la divisione sembra essere più lenta di circa il 1100%.

Una differenza non da poco, che porta a concludere che è sempre meglio usare, quando possibile, la moltiplicazione anziché la divisione. Ad esempio, quando devi dividere il numero per 2, è meglio moltiplicarlo per 0,5.