Newsgroup: it.comp.lang.c
Titolo: Divisione di un intero con segno *con* arrotondamento

Se non sbaglio, in C la divisione tra interi è sempre con troncamento.
  int x = 3;
  int y = x / 4;  /* y vale 0 */
Per fare un arrotondamento avevo pensato di fare così:
  if (x & 0x02) {
    y = x / 4 + 1;
  } else {
    y = x / 4;
  }
In questo modo i numeri che, in binario, finiscono con 00/01 sono
troncati, mentre quelli che finiscono con 10/11 vengono arrotondati
per eccesso.
Naturalmente il meccanismo non funziona se i numeri sono negativi.
Come posso fare per ottenere lo stesso risultato anche con i numeri
negativi? Cioè:
  ...
  6 -> 2
  5 -> 1
  4 -> 1
  3 -> 1
  2 -> 1
  1 -> 0
  0 -> 0
 -1 -> 0
 -2 -> 0
 -3 -> -1
 -4 -> -1
 -5 -> -1
 -6 -> -1
 ...
Naturalmente mi servirebbe un algoritmo che andasse bene indipendente
dal divisore (rimanendo sempre una potenza del 2).

 [...]
Ciao  Manlio

 ?
Perchè il processore/compilatore su cui gira questo programma non ha i
float e non vorrei
usarli per una questione di prestazioni (si tratta di un
mircocontrollore a 16MHz).

 mircocontrollore a 16MHz).
toh, ho lo stesso identico sputato problema, ora in questo
momento in cui scrivo :((((
-- 
Una volta ho chiesto la differenza tra l'incesto
e l'incendio. Non credo di aver capito troppo bene
perché dopo un po' ho dato fuoco a mia sorella.

pozz schrieb:

claudio:~/dummy> cat main.c
#include <stdio.h>
/* y deve essere una potenza del 2 */
int troncamento(int x, int y)
{
        int sign = x < 0 ? x=-x-1, -1 : 1;
        return sign * (x & y >> 1 ? x / y + 1 : x / y);
}
int main()
{
        for(int i = 6; i >= -6; i--)
                printf("%2d -> %2d\n", i, troncamento(i, 4));
        return 0;
}
claudio:~/dummy> make
gcc   -Wall -lm -std=c99 -o dummy main.c
claudio:~/dummy> ./dummy
 6 ->  2
 5 ->  1
 4 ->  1
 3 ->  1
 2 ->  1
 1 ->  0
 0 ->  0
-1 ->  0
-2 ->  0
-3 -> -1
-4 -> -1
-5 -> -1
-6 -> -1
Saluti della germania.
cc

    if (x&  0x02) {

Ciao,
non so se ho compreso bene la domanda ma io farei 
così:
int dvd, dvo, p, a, tot, r, s;
dvd = -6;  /* dividendo */
dvo = 4;   /* divisore  */
dvd *= 10;
p = dvd / dvo;
s = (p > 0) ? 1 : -1;
tot=(p / 10) + (((abs(p) % 10) >= 5) ? 1 * s : 0);


Ciao,
così però non funziona in tutte le architetture 
mentre ciò che ho proposto nella risposta 
precedente, dovrebbe.

 dal divisore (rimanendo sempre una potenza del 2).
offset = divider/2;
if(x >= 0)
{
  y = (x+offset)/divider;
}
else
{
  y = (x-offset)/divider;
}
Non l'ho provato.
Ciao Jack

dMaxime schrieb:

Ups, dove non funziona? Non vedo un problema.
cc

 cc
Nulla di grave ma siccome l'op parlava di 
embedded, tu giochi coi bit e la probabilità di 
trovare qualche micro che inverta msb e lsb è alta...
Lasciando fare al compilatore invece il problema 
può essere ignorato (l'ho riscritta un po' meno di 
getto e provata):
int DivA(int dvd, int dvo)
{
    const int Ten = 10;
    int iDvd = dvd * Ten, ParzT, Sign;
    ParzT = iDvd / dvo;
    Sign = (ParzT > 0) ? 1 : -1;
    return (ParzT / Ten) + (((abs(ParzT) % Ten) >= 
5) ? Sign : 0);
}
Ho trovato routine simili in parecchie librerie.
Un saluto.

 può essere ignorato [...]
Scusa, ma non sono ancora convinto. Gioco poco coi bit e dove lo facio
e dove vedo un rischio, lo standard va con me.
6.5.7 Bitwise shift operators
5       The result of E1 >> E2 is E1 right-shifted E2 bit positions.
        If E1 has an unsigned type or if E1 has a signed type and a
        nonnegative value, the value of the result is the integral
        part of the quotient of E1 / 2[potenza]E2. [...]
Grazie per i tuoi indicazioni.
cc


Colpa mia parlavo di byte, non di bit.

 per eccesso.
[...]
Questa regola può funzionare se il divisore è proprio 4, ma non ha
valenza generale:
ad esempio il numero 26 (11010) dovrebbe appartenere alla categoria dei
numeri che, divisi per una potenza del 2, saranno approssimati per
eccesso. Ma 26 / 2^3 = 3.25 che non penso tu voglia approssimare a 4.
-- 
Non puoi insegnare qualcosa ad un uomo. You cannot teach a man anything.
Lo puoi solo aiutare   --  Galileo  Galilei  --   You can only help him
a scoprirla dentro di sé.                        discover it in himself.
Vincenzo Mercuri

 negativi?
Che ne dite di utilizzare la funzione div() e la struttura div_t 
definita in stdlib.h? Qualcosa del genere:
int
round(int num, int den)
{
   int res;
   div_t d = div(num, den);
   res = d.quot;
   if (((d.rem > 0) && (d.rem >= den/2))
       || ((d.rem < 0) && (d.rem < -den / 2))) {
     res += (d.rem > 0) ? +1 : -1;
   }
   return res;
}
Sicuramente funziona se il denominatore è positivo (mentre il numeratore 
può essere anche negativo).
Sembra una routine molto semplice...

 dvd *= 10;
La fregatura č che qui si riduce di 10 volte il valore del numeratore. 
Per interi a 16 bit, potrei utilizzare al massimo circa 3000 che non mi 
basta.


 Sembra una routine molto semplice...
A me sembra che ti siano stati dati degli aiuti validissimi. Se ad
ogni post aggiungi delle nuove condizioni e nuove richieste non credo
che finiremo mai di risolvere il problema. Una regola d'oro se vuoi
ottenere un aiuto č di essere chiaro fin dall'inizio su cosa debba
fare realmente il tuo programma: quali tipi di dato usare, quali siano
i vincoli piů restrittivi del tuo hardware, del compilatore ecc...
č veramente difficile dirti come pilotare il tuo veicolo se non dici
almeno quante ruote ha (se le ha).
