6 Stimmen

Zählen von Brüchen über ganze Zahlen

Ich erhalte eine ganze Zahl, die einen Dollarbetrag in Bruchteilen darstellt. Ich möchte einen Algorithmus, der diese Zahlen addieren kann, ohne sie zu analysieren und in Zweier- oder Dezimalzahlen umzuwandeln.

Ich erhalte zum Beispiel die ganze Zahl 50155, was 50 und 15,5/32 Dollar bedeutet. Ich erhalte dann 10210, was 10 und 21/32 Dollar entspricht. Also 50 15,5/32 + 10 21/32 = 61 4,5/32, also:

50155 + 10210 = 61045

Auch dies möchte ich vermeiden:

int a = 50155;
int b = a / 1000;
float c = a % 1000;
float d = b;
d += c / 320f;
// d = 50.484375

Das wäre mir viel lieber:

int a = 50155;
int b = 10210;
int c = MyClass.Add(a.b); // c = 61045
...
public int Add(int a, int b)
{
    // ?????
}

Vielen Dank im Voraus für die Hilfe!

4voto

Jon Skeet Punkte 1325502

Nun, ich glaube nicht, dass Sie Gleitkomma verwenden müssen...

public static int Add(int a, int b)
{
    int firstWhole = a / 1000;
    int secondWhole = b / 1000;
    int firstFraction = a % 1000; 
    int secondFraction = b % 1000;
    int totalFraction = firstFraction + secondFraction;
    int totalWhole = firstWhole + secondWhole + (totalFraction / 320);
    return totalWhole * 1000 + (totalFraction % 320);
}

Alternativ können Sie auch eine benutzerdefinierte Struktur erstellen, die in Ihr Integer-Format konvertieren kann und den +-Operator überlädt. Das würde Ihnen erlauben, lesbareren Code zu schreiben, der nicht versehentlich zu andere Ganzzahlen werden in diesem etwas merkwürdigen Format behandelt.

EDIT: Wenn Sie gezwungen sind, sich an ein "Single Integer"-Format zu halten, es aber etwas anpassen können, sollten Sie in Erwägung ziehen, 512 anstelle von 1000 zu verwenden. Auf diese Weise können Sie eine einfache Maske und Verschiebung verwenden:

public static int Add(int a, int b)
{
    int firstWhole = a >> 9;
    int secondWhole = b >> 9;
    int firstFraction = a & 0x1ff
    int secondFraction = b & 0x1ff;
    int totalFraction = firstFraction + secondFraction;
    int totalWhole = firstWhole + secondWhole + (totalFraction / 320);
    return (totalWhole << 9) + (totalFraction % 320);
}

Es gibt immer noch das Herumhantieren mit 320, aber es ist zumindest etwas besser geworden.

3voto

Jerry Coffin Punkte 452852

Trennen Sie die Schnur in den Teil, der ganze Dollar darstellt, und den Teil, der Bruchteile von Dollar darstellt. Für letztere ist es wahrscheinlich einfacher, sie als 105 Dreihundertzwanzigstel eines Dollars zu behandeln, anstatt sie als 10,5 Zweiunddreißigstel eines Dollars zu behandeln (d. h. beide mit zehn zu multiplizieren, damit der Zähler immer eine ganze Zahl ist).

Von dort aus ist das Rechnen recht einfach (wenn auch etwas mühsam zu schreiben): Addiere die Brüche. Wenn das mehr als ein ganzer Dollar ist, nimm einen Dollar dazu (und ziehe 320 vom Bruchteil ab). Dann addiere die ganzen Dollar. Das Gleiche gilt für die Subtraktion - allerdings müssen Sie in diesem Fall die Kreditaufnahme statt des Übertrags in Betracht ziehen.

3voto

mjv Punkte 70143

bearbeiten :
Diese Antwort legt nahe, dass man sich von der Float-Arithmetik "fernhält". Überraschenderweise gab der Auftraggeber an, dass seine auf Fließkommazahlen basierende Logik (die aus geschützten Gründen nicht angezeigt wird) doppelt so schnell war wie die nachstehende Integer-Modulo-Lösung! Das zeigt, dass FPUs doch nicht so schlecht sind...

Bleiben Sie auf jeden Fall von Schwimmern weg (für dieses spezielle Problem). Ganzzahlige Arithmetik ist effizienter und führt nicht zu Rundungsfehlern.

So etwas wie das Folgende sollte funktionieren
Anmerkung: Wie geschrieben, wird angenommen, dass A und B positiv sind.

int AddMyOddlyEncodedDollars (int A, int B) {
  int sum;
  sum = A + B
  if (sum % 1000 < 320);
     return sum
  else
     return sum + 1000 - 320;
}

bearbeiten : Über die Effizienz des Modulo-Operators in C
Das hängt sehr stark vom Compiler ab... Da der Modulo-Wert zur Kompilierzeit bekannt ist, würde ich erwarten, dass die meisten modernen Compiler den Ansatz "Multiplizieren [mit dem Kehrwert] und Verschieben" wählen, und das ist schnell.
Diese Besorgnis über die Leistung (bei diesem ziemlich ausgeklügelten Format) ist ein Aufruf zur verfrühten Optimierung, aber andererseits habe ich schon Software in der Finanzbranche gesehen, die mächtig optimiert wurde (um es höflich auszudrücken), und das zu Recht.

2voto

plinth Punkte 46829

Als Lernpunkt wird diese Darstellung als " Festpunkt ". Es gibt eine Reihe von Implementierungen, die Sie sich ansehen können. Ich würde dringend empfehlen, dass Sie NICHT int als Top-Level-Datentyp verwenden, sondern stattdessen einen Typ namens Fixed erstellen, der die Operationen kapselt. Dadurch wird die Anzahl der Fehler verringert, wenn Sie versehentlich einen einfachen int zu einer Festkommazahl hinzufügen, ohne sie vorher zu skalieren, oder eine Zahl skalieren und vergessen, die Skalierung zu entfernen.

1voto

Antti Huima Punkte 24441

Sieht für mich nach einer seltsamen Kodierung aus.

Wie auch immer, wenn das Format 10-Basis Nxxx ist, wobei N eine ganze Zahl ist, die ganze Dollar bezeichnet, und xxx interpretiert wird als

(xxx / 320)

und Sie wollen sie addieren, müssen Sie nur den Übertrag durchführen, wenn xxx 320 überschreitet:

int a = ..., b = ...; // dollar amounts
int c = (a + b); // add together
// Calculate carry
int carry = (c % 1000) / 320; // integer division
c += carry * 1000;
c -= carry * 320;
// done

Hinweis: Dies funktioniert, weil bei korrekter Kodierung von a und b die Nachkommastellen zusammen höchstens 638 ergeben und somit kein "Überlauf" zum ganzen Dollar-Teil stattfindet.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X