it-swarm.com.de

Runden der Ganzzahlteilung (statt abschneiden)

Ich war neugierig zu wissen, wie ich eine Zahl zur nächsten runden kann zehntel ganze Zahl. Wenn ich zum Beispiel

int a = 59 / 4;

das wäre 14,75, berechnet in Fließkommazahl; Wie kann ich die Nummer als 15 in "a" speichern?

60
Dave
int a = 59.0f / 4.0f + 0.5f;

Dies funktioniert nur beim Zuweisen eines int, da alles nach dem '.' Verworfen wird.

Edit: Diese Lösung funktioniert nur in den einfachsten Fällen. Eine robustere Lösung wäre:

unsigned int round_closest(unsigned int dividend, unsigned int divisor)
{
    return (dividend + (divisor / 2)) / divisor;
}
38
0xC0DEFACE

Die Standardsprache für die Ganzzahlrundung lautet:

int a = (59 + (4 - 1)) / 4;

Sie addieren den Divisor minus eins zur Dividende.

112

Ein Code, der für jedes Zeichen in Dividende und Divisor funktioniert:

int divRoundClosest(const int n, const int d)
{
  return ((n < 0) ^ (d < 0)) ? ((n - d/2)/d) : ((n + d/2)/d);
}

Wenn Sie ein Makro bevorzugen:

#define DIV_ROUND_CLOSEST(n, d) ((((n) < 0) ^ ((d) < 0)) ? (((n) - (d)/2)/(d)) : (((n) + (d)/2)/(d)))

Das Linux-Kernel-Makro DIV_ROUND_CLOSEST funktioniert nicht für negative Divisoren!

41
ericbn

Sie sollten stattdessen etwas wie folgt verwenden:

int a = (59 - 1)/ 4 + 1;

Ich gehe davon aus, dass Sie wirklich etwas allgemeineres versuchen:

int divide(x, y)
{
   int a = (x -1)/y +1;

   return a;
}

x + (y-1) kann zum Überlaufen führen, was zu einem falschen Ergebnis führt; x - 1 wird jedoch nur unterlaufen, wenn x = min_int ...

23
WayneJ

(Bearbeitet) Das Runden von Ganzzahlen mit Gleitkommazahlen ist die einfachste Lösung für dieses Problem. Abhängig von der eingestellten Problematik kann dies jedoch möglich sein. Beispielsweise kann in eingebetteten Systemen die Gleitkomma-Lösung zu kostspielig sein.

Dies mit Integer-Mathematik zu tun, erweist sich als etwas schwierig und wenig intuitiv. Die erste veröffentlichte Lösung funktionierte für das Problem, für das ich sie verwendet hatte, in Ordnung, aber nachdem die Ergebnisse über den Bereich von ganzen Zahlen charakterisiert worden waren, stellte sich heraus, dass sie im Allgemeinen sehr schlecht waren. Das Durchsuchen mehrerer Bücher über Bit Twiddling und eingebettete Mathematik liefert nur wenige Ergebnisse. Ein paar Notizen. Erstens habe ich nur auf positive ganze Zahlen getestet, meine Arbeit beinhaltet keine negativen Zähler oder Nenner. Zweitens ist der ausführliche Test von 32-Bit-Ganzzahlen rechenintensiv. Ich habe also mit 8-Bit-Ganzzahlen begonnen und bin mir dann sicher, dass ich mit 16-Bit-Ganzzahlen ähnliche Ergebnisse erzielt habe.

Ich habe mit den beiden zuvor vorgeschlagenen Lösungen begonnen:

#define DIVIDE_WITH_ROUND(N, D) (((N) == 0) ? 0:(((N * 10)/D) + 5)/10)

#define DIVIDE_WITH_ROUND(N, D) (N == 0) ? 0:(N - D/2)/D + 1;

Mein Gedanke war, dass die erste Version mit großen Zahlen überläuft und die zweite mit kleinen Zahlen unterläuft. Ich habe 2 Dinge nicht berücksichtigt. 1.) Das 2. Problem ist tatsächlich rekursiv, da Sie D/2 richtig runden müssen, um die richtige Antwort zu erhalten. 2.) Im ersten Fall läuft man oft über und dann unter, wobei sich die beiden gegenseitig aufheben. Hier ist eine Fehlerdarstellung der beiden (falschen) Algorithmen:Divide with Round1 8 bit x=numerator y=denominator

Dieses Diagramm zeigt, dass der erste Algorithmus nur für kleine Nenner (0 <d <10) falsch ist. Unerwartet verarbeitet es tatsächlich große Zähler besser als die 2. Version.

Hier ist eine Darstellung des 2. Algorithmus: 8 bit signed numbers 2nd algorithm.

Wie erwartet schlägt dies bei kleinen Zählern fehl, aber auch bei größeren Zählern als in der 1. Version.

Dies ist eindeutig der bessere Ausgangspunkt für eine korrekte Version:

#define DIVIDE_WITH_ROUND(N, D) (((N) == 0) ? 0:(((N * 10)/D) + 5)/10)

Wenn Ihr Nenner> 10 ist, funktioniert dies korrekt.

Für D == 1 wird ein Sonderfall benötigt, geben Sie einfach N zurück. Für D == 2 wird ein Sonderfall benötigt, = N/2 + (N & 1) // Aufrunden, wenn ungerade.

D> = 3 hat auch Probleme, wenn N groß genug ist. Es stellt sich heraus, dass größere Nenner nur Probleme mit größeren Zählern haben. Für 8 Bit vorzeichenbehaftete Zahlen sind die Problempunkte

if (D == 3) && (N > 75))
else if ((D == 4) && (N > 100))
else if ((D == 5) && (N > 125))
else if ((D == 6) && (N > 150))
else if ((D == 7) && (N > 175))
else if ((D == 8) && (N > 200))
else if ((D == 9) && (N > 225))
else if ((D == 10) && (N > 250))

(Rückgabe D/N für diese)

Im Allgemeinen ist die Spitze, an der ein bestimmter Zähler schlecht wird, also irgendwo in der Nähe
N > (MAX_INT - 5) * D/10

Dies ist nicht genau, aber nahe. Wenn Sie mit 16-Bit-Zahlen oder größeren Zahlen arbeiten, ist der Fehler <1%, wenn Sie für diese Fälle nur eine C-Division (Kürzung) durchführen.

Für 16-Bit-Zahlen mit Vorzeichen wären die Tests

if ((D == 3) && (N >= 9829))
else if ((D == 4) && (N >= 13106))
else if ((D == 5) && (N >= 16382))
else if ((D == 6) && (N >= 19658))
else if ((D == 7) && (N >= 22935))
else if ((D == 8) && (N >= 26211))
else if ((D == 9) && (N >= 29487))
else if ((D == 10) && (N >= 32763))

Natürlich würde für vorzeichenlose ganze Zahlen MAX_INT durch MAX_UINT ersetzt. Ich bin mir sicher, dass es eine genaue Formel für die Bestimmung des größten N gibt, das für ein bestimmtes D und die Anzahl der Bits funktioniert, aber ich habe keine Zeit mehr, um an diesem Problem zu arbeiten ...

(Ich vermisse dieses Diagramm im Moment, ich werde es später bearbeiten und hinzufügen.) Dies ist ein Diagramm der 8-Bit-Version mit den oben angegebenen Sonderfällen:! [8-Bit mit Sonderfällen für 0 < N <= 10

Beachten Sie, dass für 8 Bit der Fehler 10% oder weniger beträgt, für alle Fehler im Diagramm 16 Bit <0,1%.

10
WayneJ

Wie geschrieben, führen Sie eine Ganzzahlarithmetik durch, bei der die Dezimalergebnisse automatisch abgeschnitten werden. Um eine Gleitkomma-Arithmetik durchzuführen, ändern Sie entweder die Konstanten in Gleitkommawerte:

int a = round(59.0 / 4);

Oder gießen Sie sie in eine float oder einen anderen Gleitkommatyp:

int a = round((float)59 / 4);

In jedem Fall müssen Sie die letzte Rundung mit der Funktion round() im Header math.h durchführen. Stellen Sie daher sicher, dass Sie #include <math.h> verwenden und einen C99-kompatiblen Compiler verwenden.

7
Chris Lutz
int a, b;
int c = a / b;
if(a % b) { c++; }

Wenn Sie prüfen, ob ein Rest vorhanden ist, können Sie den Quotienten der Ganzzahldivision manuell aufrunden.

4
user3707766

Vom Linux-Kernel (GPLv2):

/*
 * Divide positive or negative dividend by positive divisor and round
 * to closest integer. Result is undefined for negative divisors and
 * for negative dividends if the divisor variable type is unsigned.
 */
#define DIV_ROUND_CLOSEST(x, divisor)(          \
{                           \
    typeof(x) __x = x;              \
    typeof(divisor) __d = divisor;          \
    (((typeof(x))-1) > 0 ||             \
     ((typeof(divisor))-1) > 0 || (__x) > 0) ?  \
        (((__x) + ((__d) / 2)) / (__d)) :   \
        (((__x) - ((__d) / 2)) / (__d));    \
}                           \
)
4
PauliusZ
#define CEIL(a, b) (((a) / (b)) + (((a) % (b)) > 0 ? 1 : 0))

Ein weiteres nützliches MAKROS (MUSS HABEN):

#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
#define ABS(a)     (((a) < 0) ? -(a) : (a))
3
Magnetron

Hier ist meine Lösung. Ich mag es, weil ich es lesbarer finde und weil es keine Verzweigungen hat (weder wenn noch ternaries).

int32_t divide(int32_t a, int32_t b) {
  int32_t resultIsNegative = ((a ^ b) & 0x80000000) >> 31;
  int32_t sign = resultIsNegative*-2+1;
  return (a + (b / 2 * sign)) / b;
}

Vollständiges Testprogramm, das das beabsichtigte Verhalten veranschaulicht:

#include <stdint.h>
#include <assert.h>

int32_t divide(int32_t a, int32_t b) {
  int32_t resultIsNegative = ((a ^ b) & 0x80000000) >> 31;
  int32_t sign = resultIsNegative*-2+1;
  return (a + (b / 2 * sign)) / b;
}

int main() {
  assert(divide(0, 3) == 0);

  assert(divide(1, 3) == 0);
  assert(divide(5, 3) == 2);

  assert(divide(-1, 3) == 0);
  assert(divide(-5, 3) == -2);

  assert(divide(1, -3) == 0);
  assert(divide(5, -3) == -2);

  assert(divide(-1, -3) == 0);
  assert(divide(-5, -3) == 2);
}

Ausleihe von @ericbn definiere ich gerne

#define DIV_ROUND_INT(n,d) ((((n) < 0) ^ ((d) < 0)) ? (((n) - (d)/2)/(d)) : (((n) + (d)/2)/(d)))
or if you work only with unsigned ints
#define DIV_ROUND_UINT(n,d) ((((n) + (d)/2)/(d)))
2
zevero
int divide(x,y){
 int quotient = x/y;
 int remainder = x%y;
 if(remainder==0)
  return quotient;
 int tempY = divide(y,2);
 if(remainder>=tempY)
  quotient++;
 return quotient;
}

zB 59/4 Quotient = 14, TempY = 2, Rest = 3, Rest> = TempY, daher Quotient = 15;

1
Abhay Lolekar
double a=59.0/4;
int b=59/4;
if(a-b>=0.5){
    b++;
}
printf("%d",b);
  1. der exakte Float-Wert von 59,0/4 sei x (hier ist es 14,750000)
  2. kleinste ganze Zahl kleiner als x sei y (hier ist es 14)
  3. wenn x-y <0,5 ist, ist y die Lösung
  4. ansonsten ist y + 1 die Lösung
1
Siva Dhatra

Wenn Sie positive Ganzzahlen teilen, können Sie sie nach oben verschieben, die Division durchführen und dann das Bit rechts vom echten b0 überprüfen. Mit anderen Worten, 100/8 ist 12,5, würde aber 12 zurückgeben. Wenn Sie (100 << 1)/8 tun, können Sie b0 überprüfen und dann aufrunden, nachdem Sie das Ergebnis nach unten verschoben haben.

0
bryan

versuchen Sie es mit der math ceiling-Funktion, die das Aufrunden macht. Math Ceil !

0
Samuel Santos

Im Folgenden wird der Quotient für positive und negative Operanden OHNE Gleitkomma- oder bedingte Verzweigungen korrekt auf die nächste Ganzzahl gerundet (siehe Assembly-Ausgabe unten). Nimmt die Komplement-Ganzzahlen von N-Bit 2 an.

#define ASR(x) ((x) < 0 ? -1 : 0)  // Compiles into a (N-1)-bit arithmetic shift right
#define ROUNDING(x,y) ( (y)/2 - (ASR((x)^(y)) & (y)))

int RoundedQuotient(int x, int y)
   {
   return (x + ROUNDING(x,y)) / y ;
   }

Der Wert von ROUNDING hat das gleiche Vorzeichen wie die Dividende (x) und die Hälfte der Größe des Divisors (y) . Das Hinzufügen von RUNDUNG zur Dividende erhöht somit deren Größe, bevor die Ganzzahldivision den resultierenden Quotienten abschneidet. Hier ist die Ausgabe des GCC-Compilers mit -O3-Optimierung für einen 32-Bit ARM Cortex-M4-Prozessor:

RoundedQuotient:                // Input parameters: r0 = x, r1 = y
    eor     r2, r1, r0          // r2 = x^y
    and     r2, r1, r2, asr #31 // r2 = ASR(x^y) & y
    add     r3, r1, r1, lsr #31 // r3 = (y < 0) ? y + 1 : y
    rsb     r3, r2, r3, asr #1  // r3 = y/2 - (ASR(x^y) & y)
    add     r0, r0, r3          // r0 = x + (y/2 - (ASR(x^y) & y)
    sdiv    r0, r0, r1          // r0 = (x + ROUNDING(x,y)) / y
    bx      lr                  // Returns r0 = rounded quotient
0
Dan Lewis

Einige Alternativen zur Division durch 4

return x/4 + (x/2 % 2);
return x/4 + (x % 4 >= 2)

Oder im Allgemeinen Division durch eine Potenz von 2

return x/y + x/(y/2) % 2;    // or
return (x >> i) + ((x >> i - 1) & 1);  // with y = 2^i

Es funktioniert durch Aufrunden, wenn der Bruchteil ≤ 0,5 ist, d. H. Die erste Ziffer ≤ Basis/2. In der Binärdarstellung entspricht dies dem Hinzufügen des ersten gebrochenen Bits zum Ergebnis

Diese Methode hat einen Vorteil in Architekturen mit einem Flagregister, da das Übertragsflag das letzte Bit enthält, das herausgeschoben wurde . Zum Beispiel auf x86 kann es optimiert werden in

shr eax, i
adc eax, 0

Es ist auch leicht zu erweitern, um vorzeichenbehaftete Ganzzahlen zu unterstützen. Beachten Sie, dass der Ausdruck für negative Zahlen ist

(x - 1)/y + ((x - 1)/(y/2) & 1)

mit können wir es sowohl für positive als auch für negative Werte arbeiten lassen

int t = x + (x >> 31);
return (t >> i) + ((t >> i - 1) & 1);
0
phuclv

Für einige Algorithmen benötigen Sie eine konsistente Tendenz, wenn "am nächsten" ein Gleichstand ist.

// round-to-nearest with mid-value bias towards positive infinity
int div_nearest( int n, int d )
   {
   if (d<0) n*=-1, d*=-1;
   return (abs(n)+((d-(n<0?1:0))>>1))/d * ((n<0)?-1:+1);
   }

Dies funktioniert unabhängig vom Vorzeichen des Zählers oder Nenners.


Wenn Sie die Ergebnisse von round(N/(double)D) (Gleitkommadivision und Rundung) abgleichen möchten, haben wir einige Variationen, die alle zu den gleichen Ergebnissen führen:

int div_nearest( int n, int d )
   {
   int r=(n<0?-1:+1)*(abs(d)>>1); // eliminates a division
// int r=((n<0)^(d<0)?-1:+1)*(d/2); // basically the same as @ericbn
// int r=(n*d<0?-1:+1)*(d/2); // small variation from @ericbn
   return (n+r)/d;
   }

Hinweis: Die relative Geschwindigkeit von (abs(d)>>1) vs. (d/2) hängt wahrscheinlich von der Plattform ab.

0
nobar