Wie man Zahlen auf das nächste 0,05 oder andere Inkremente in JavaScript rundet
Erfahren Sie, wie man Währungen und Zahlen auf bestimmte Inkremente wie 0,05 oder 0,10 für Länder ohne Kleingeld rundet
Einführung
Ein Kunde in Kanada fügt Artikel zu einem Warenkorb hinzu, die insgesamt $11,23 kosten, aber die endgültige Belastung zeigt $11,25 an. Dies geschieht, weil Kanada 2013 den Penny abgeschafft hat, sodass Bargeldtransaktionen auf die nächsten 0,05 gerundet werden. Ihre Anwendung muss Preise anzeigen, die dem entsprechen, was Kunden tatsächlich bezahlen.
Das Runden auf bestimmte Inkremente löst dieses Problem. Sie können Zahlen auf den nächsten Nickel (0,05), Dime (0,10) oder jedes andere Inkrement runden. Dies gilt für Währungsformatierung, Messsysteme und jedes Szenario, in dem Werte mit bestimmten Inkrementen übereinstimmen müssen.
Dieser Leitfaden zeigt Ihnen, wie Sie Zahlen mit JavaScript auf benutzerdefinierte Inkremente runden können, einschließlich sowohl manueller Implementierung als auch der modernen Intl.NumberFormat-API.
Warum auf bestimmte Inkremente runden
Viele Länder haben Münzen mit kleinen Nennwerten aus praktischen Gründen abgeschafft. Die Herstellung und Handhabung dieser Münzen kostet mehr als ihr Nennwert. Bargeldtransaktionen in diesen Ländern werden auf die nächste verfügbare Münze gerundet.
Länder, die 0,05-Rundung für Barzahlungen verwenden:
- Kanada (Penny 2013 abgeschafft)
- Australien (1- und 2-Cent-Münzen abgeschafft)
- Niederlande (rundet auf die nächsten 0,05 Euro)
- Belgien (rundet auf die nächsten 0,05 Euro)
- Irland (rundet auf die nächsten 0,05 Euro)
- Italien (rundet auf die nächsten 0,05 Euro)
- Finnland (rundet auf die nächsten 0,05 Euro)
- Schweiz (kleinste Münze ist 0,05 CHF)
Länder, die 0,10-Rundung verwenden:
- Neuseeland (5-Cent-Münze 2006 abgeschafft)
Die Vereinigten Staaten planen, die Produktion von Pennys bis Anfang 2026 einzustellen, was eine 0,05-Rundung für Bargeldtransaktionen erfordern wird.
Die Rundung bei Bargeld gilt nur für den endgültigen Transaktionsbetrag, nicht für einzelne Artikel. Elektronische Zahlungen bleiben exakt, da keine physischen Münzen den Besitzer wechseln.
Abgesehen von Währungen könnten Sie benutzerdefinierte Inkremente benötigen für:
- Messsysteme mit spezifischen Präzisionsanforderungen
- Wissenschaftliche Berechnungen, gerundet auf Instrumentenpräzision
- Benutzeroberflächen-Steuerelemente, die auf bestimmte Werte einrasten
- Preisstrategien, die psychologische Preispunkte verwenden
Das mathematische Konzept hinter der Inkrementrundung
Rundung auf ein Inkrement bedeutet, das nächstgelegene Vielfache dieses Inkrements zu finden. Die Formel teilt die Zahl durch das Inkrement, rundet auf die nächste ganze Zahl und multipliziert dann wieder mit dem Inkrement.
roundedValue = Math.round(value / increment) * increment
Zum Beispiel, Rundung von 11,23 auf das nächste 0,05:
- Division: 11,23 / 0,05 = 224,6
- Rundung: Math.round(224,6) = 225
- Multiplikation: 225 * 0,05 = 11,25
Die Division wandelt das Inkrement in ganzzahlige Schritte um. Die Rundung von 224,6 auf 225 findet den nächsten Schritt. Die Multiplikation konvertiert zurück zur ursprünglichen Skala.
Für 11,27 gerundet auf das nächste 0,05:
- Division: 11,27 / 0,05 = 225,4
- Rundung: Math.round(225,4) = 225
- Multiplikation: 225 * 0,05 = 11,25
Sowohl 11,23 als auch 11,27 werden auf 11,25 gerundet, da sie innerhalb von 0,025 dieses Wertes liegen. Der Mittelpunkt 11,25 bleibt bei 11,25.
Manuelle Implementierung der Inkrementrundung
Erstellen Sie eine Funktion, die eine beliebige Zahl auf ein bestimmtes Inkrement rundet:
function roundToIncrement(value, increment) {
return Math.round(value / increment) * increment;
}
console.log(roundToIncrement(11.23, 0.05)); // 11.25
console.log(roundToIncrement(11.27, 0.05)); // 11.25
console.log(roundToIncrement(11.28, 0.05)); // 11.30
console.log(roundToIncrement(4.94, 0.10)); // 4.90
console.log(roundToIncrement(4.96, 0.10)); // 5.00
Dies funktioniert für jedes Inkrement, nicht nur für Währungen:
console.log(roundToIncrement(17, 5)); // 15
console.log(roundToIncrement(23, 5)); // 25
console.log(roundToIncrement(2.7, 0.5)); // 2.5
JavaScript-Fließkomma-Arithmetik kann kleine Präzisionsfehler einführen. Für Währungsberechnungen, bei denen Präzision wichtig ist, multiplizieren Sie Werte mit einer Zehnerpotenz, um mit ganzen Zahlen zu arbeiten:
function roundToIncrementPrecise(value, increment) {
// Anzahl der Dezimalstellen im Inkrement ermitteln
const decimals = (increment.toString().split('.')[1] || '').length;
const multiplier = Math.pow(10, decimals);
// In Ganzzahlen umwandeln
const valueInt = Math.round(value * multiplier);
const incrementInt = Math.round(increment * multiplier);
// Runden und zurückkonvertieren
return Math.round(valueInt / incrementInt) * incrementInt / multiplier;
}
console.log(roundToIncrementPrecise(11.23, 0.05)); // 11.25
Math.round() verwendet die Aufrundung bei Halbzahlen, wobei Werte genau zwischen zwei Inkrementen aufgerundet werden. Einige Anwendungen benötigen verschiedene Rundungsmodi:
function roundToIncrementWithMode(value, increment, mode = 'halfUp') {
const divided = value / increment;
let rounded;
switch (mode) {
case 'up':
rounded = Math.ceil(divided);
break;
case 'down':
rounded = Math.floor(divided);
break;
case 'halfDown':
rounded = divided % 1 === 0.5 ? Math.floor(divided) : Math.round(divided);
break;
case 'halfUp':
default:
rounded = Math.round(divided);
break;
}
return rounded * increment;
}
console.log(roundToIncrementWithMode(11.225, 0.05, 'halfUp')); // 11.25
console.log(roundToIncrementWithMode(11.225, 0.05, 'halfDown')); // 11.20
console.log(roundToIncrementWithMode(11.23, 0.05, 'up')); // 11.25
console.log(roundToIncrementWithMode(11.23, 0.05, 'down')); // 11.20
Verwendung von Intl.NumberFormat mit roundingIncrement
Moderne Browser unterstützen die Option roundingIncrement in Intl.NumberFormat, die beim Formatieren von Zahlen automatisch die Rundung auf bestimmte Inkremente durchführt.
Grundlegende Verwendung für die Nickel-Rundung des kanadischen Dollars:
const formatter = new Intl.NumberFormat('en-CA', {
style: 'currency',
currency: 'CAD',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
roundingIncrement: 5
});
console.log(formatter.format(11.23)); // CA$11.25
console.log(formatter.format(11.27)); // CA$11.25
console.log(formatter.format(11.28)); // CA$11.30
Die Option roundingIncrement akzeptiert nur bestimmte Werte:
- 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500, 5000
Für eine Rundung auf 0,05 setzen Sie maximumFractionDigits: 2 und roundingIncrement: 5. Für eine Rundung auf 0,10 setzen Sie maximumFractionDigits: 1 und roundingIncrement: 1 oder behalten Sie maximumFractionDigits: 2 mit roundingIncrement: 10 bei.
Schweizer Franken-Rundung auf 0,05:
const chfFormatter = new Intl.NumberFormat('de-CH', {
style: 'currency',
currency: 'CHF',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
roundingIncrement: 5
});
console.log(chfFormatter.format(8.47)); // CHF 8.45
console.log(chfFormatter.format(8.48)); // CHF 8.50
Neuseeland-Dollar-Rundung auf 0,10:
const nzdFormatter = new Intl.NumberFormat('en-NZ', {
style: 'currency',
currency: 'NZD',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
roundingIncrement: 10
});
console.log(nzdFormatter.format(15.94)); // $15.90
console.log(nzdFormatter.format(15.96)); // $16.00
Sie können roundingIncrement mit verschiedenen Rundungsmodi kombinieren:
const formatterUp = new Intl.NumberFormat('en-CA', {
style: 'currency',
currency: 'CAD',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
roundingIncrement: 5,
roundingMode: 'ceil'
});
console.log(formatterUp.format(11.21)); // CA$11.25 (aufgerundet)
Verfügbare Rundungsmodi:
ceil: Rundung in Richtung positiver Unendlichkeitfloor: Rundung in Richtung negativer Unendlichkeitexpand: Rundung weg von Nulltrunc: Rundung in Richtung NullhalfCeil: Halbzahlen in Richtung positiver Unendlichkeit rundenhalfFloor: Halbzahlen in Richtung negativer Unendlichkeit rundenhalfExpand: Halbzahlen weg von Null runden (Standard)halfTrunc: Halbzahlen in Richtung Null rundenhalfEven: Halbzahlen zur geraden Zahl runden (kaufmännische Rundung)
Wichtige Einschränkungen für roundingIncrement:
- Sowohl
minimumFractionDigitsals auchmaximumFractionDigitsmüssen denselben Wert haben - Kann nicht mit signifikanter Stellenrundung kombiniert werden
- Funktioniert nur, wenn
roundingPriorityaufautogesetzt ist (Standardeinstellung)
Überprüfen Sie, ob der Browser roundingIncrement unterstützt:
function supportsRoundingIncrement() {
try {
const formatter = new Intl.NumberFormat('en-US', {
roundingIncrement: 5,
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
const options = formatter.resolvedOptions();
return options.roundingIncrement === 5;
} catch (e) {
return false;
}
}
if (supportsRoundingIncrement()) {
// Verwenden Sie Intl.NumberFormat mit roundingIncrement
} else {
// Fallback auf manuelle Rundung
}
Praktische Implementierungsbeispiele
Erstellen Sie einen Währungsformatierer, der Bargeld-Rundung basierend auf dem Gebietsschema verarbeitet:
function createCashFormatter(locale, currency) {
// Define cash rounding rules by currency
const cashRoundingRules = {
'CAD': { increment: 5, digits: 2 },
'AUD': { increment: 5, digits: 2 },
'EUR': { increment: 5, digits: 2 }, // Some eurozone countries
'CHF': { increment: 5, digits: 2 },
'NZD': { increment: 10, digits: 2 }
};
const rule = cashRoundingRules[currency];
if (!rule) {
// No special rounding needed
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency
});
}
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency,
minimumFractionDigits: rule.digits,
maximumFractionDigits: rule.digits,
roundingIncrement: rule.increment
});
}
const cadFormatter = createCashFormatter('en-CA', 'CAD');
console.log(cadFormatter.format(47.83)); // CA$47.85
const usdFormatter = createCashFormatter('en-US', 'USD');
console.log(usdFormatter.format(47.83)); // $47.83 (no rounding)
Verarbeiten Sie sowohl Bargeld- als auch Kartenzahlungen:
function formatPaymentAmount(amount, currency, locale, paymentMethod) {
if (paymentMethod === 'cash') {
const formatter = createCashFormatter(locale, currency);
return formatter.format(amount);
} else {
// Electronic payments use exact amounts
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency
}).format(amount);
}
}
console.log(formatPaymentAmount(11.23, 'CAD', 'en-CA', 'cash')); // CA$11.25
console.log(formatPaymentAmount(11.23, 'CAD', 'en-CA', 'card')); // CA$11.23
Berechnen Sie den tatsächlich zu berechnenden Betrag:
function calculateCashTotal(items, currency) {
const subtotal = items.reduce((sum, item) => sum + item.price, 0);
// Apply cash rounding only to the final total
const cashRoundingRules = {
'CAD': 0.05,
'AUD': 0.05,
'CHF': 0.05,
'NZD': 0.10
};
const increment = cashRoundingRules[currency];
if (!increment) {
return subtotal;
}
return roundToIncrement(subtotal, increment);
}
function roundToIncrement(value, increment) {
return Math.round(value / increment) * increment;
}
const items = [
{ price: 5.99 },
{ price: 3.49 },
{ price: 1.75 }
];
console.log(calculateCashTotal(items, 'CAD')); // 11.25
console.log(calculateCashTotal(items, 'USD')); // 11.23
Verwenden Sie Inkrement-Rundung für Nicht-Währungswerte:
// Temperature display rounded to nearest 0.5 degrees
function formatTemperature(celsius) {
const rounded = roundToIncrement(celsius, 0.5);
return new Intl.NumberFormat('en-US', {
style: 'unit',
unit: 'celsius',
minimumFractionDigits: 1,
maximumFractionDigits: 1
}).format(rounded);
}
console.log(formatTemperature(22.3)); // 22.5°C
console.log(formatTemperature(22.6)); // 22.5°C
// Slider values snapped to increments
function snapToGrid(value, gridSize) {
return roundToIncrement(value, gridSize);
}
console.log(snapToGrid(47, 5)); // 45
console.log(snapToGrid(53, 5)); // 55
Wichtige Überlegungen und Sonderfälle
Wenden Sie die Bargeld-Rundung nur auf die endgültige Transaktionssumme an, nicht auf einzelne Positionen. Das separate Runden jeder Position führt zu einem anderen Ergebnis als das Runden der Summe:
const items = [1.23, 1.23, 1.23];
// Falsch: Rundung jeder einzelnen Position
const wrongTotal = items
.map(price => roundToIncrement(price, 0.05))
.reduce((sum, price) => sum + price, 0);
console.log(wrongTotal); // 3.75
// Korrekt: Rundung der Gesamtsumme
const correctTotal = roundToIncrement(
items.reduce((sum, price) => sum + price, 0),
0.05
);
console.log(correctTotal); // 3.70
Zeigen Sie den Benutzern gerundete Beträge an, speichern Sie jedoch exakte Beträge in Ihrer Datenbank. Dies bewahrt die Präzision für die Buchhaltung und ermöglicht es Ihnen, Rundungsregeln zu ändern, ohne Informationen zu verlieren:
const transaction = {
items: [
{ id: 1, price: 5.99 },
{ id: 2, price: 3.49 },
{ id: 3, price: 1.75 }
],
subtotal: 11.23, // Exakten Wert speichern
currency: 'CAD',
paymentMethod: 'cash'
};
// Anzeigebetrag berechnen
const displayAmount = roundToIncrement(transaction.subtotal, 0.05); // 11.25
Verschiedene Länder, die dieselbe Währung verwenden, können unterschiedliche Bargeld-Rundungsregeln haben. Der Euro wird in Ländern mit und ohne Bargeld-Rundung verwendet. Überprüfen Sie die spezifischen Vorschriften des jeweiligen Landes, nicht nur die Währung.
Negative Beträge werden für den Absolutwert gerundet und dann das negative Vorzeichen angewendet:
console.log(roundToIncrement(-11.23, 0.05)); // -11.25
console.log(roundToIncrement(-11.22, 0.05)); // -11.20
Sehr große Zahlen können bei Gleitkomma-Arithmetik an Präzision verlieren. Für Finanzanwendungen, die große Beträge verarbeiten, sollten Sie die Verwendung von Integer-Arithmetik mit der kleinsten Währungseinheit in Betracht ziehen:
function roundToIncrementCents(cents, incrementCents) {
return Math.round(cents / incrementCents) * incrementCents;
}
// In Cent arbeiten
const amountCents = 1123; // $11.23
const roundedCents = roundToIncrementCents(amountCents, 5); // 1125
const dollars = roundedCents / 100; // 11.25
Testen Sie Ihre Rundungsimplementierung mit Sonderfällen:
// Testfälle für 0.05-Rundung
const testCases = [
{ input: 0.00, expected: 0.00 },
{ input: 0.01, expected: 0.00 },
{ input: 0.02, expected: 0.00 },
{ input: 0.025, expected: 0.05 },
{ input: 0.03, expected: 0.05 },
{ input: 0.99, expected: 1.00 },
{ input: -0.03, expected: -0.05 },
{ input: 11.225, expected: 11.25 }
];
testCases.forEach(({ input, expected }) => {
const result = roundToIncrement(input, 0.05);
console.log(`${input} -> ${result} (expected ${expected})`);
});
Die Option roundingIncrement in Intl.NumberFormat wird in modernen Browsern unterstützt. Firefox hat die Unterstützung in Version 93 hinzugefügt. Für Anwendungen, die ältere Browser unterstützen, implementieren Sie einen Fallback:
function formatCurrency(amount, locale, currency, increment) {
try {
const formatter = new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency,
minimumFractionDigits: 2,
maximumFractionDigits: 2,
roundingIncrement: increment
});
// Überprüfen, ob roundingIncrement tatsächlich angewendet wurde
if (formatter.resolvedOptions().roundingIncrement === increment) {
return formatter.format(amount);
}
} catch (e) {
// roundingIncrement wird nicht unterstützt
}
// Fallback: manuelle Rundung
const rounded = roundToIncrement(amount, increment / 100);
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency
}).format(rounded);
}
Dokumentieren Sie Ihr Rundungsverhalten klar in benutzerorientierten Schnittstellen. Benutzer müssen verstehen, warum angezeigte Beträge von Positionssummen abweichen:
<div class="cart-summary">
<div class="line-item">Zwischensumme: $11.23</div>
<div class="line-item total">Gesamtsumme (bar): $11.25</div>
<div class="note">Barzahlungen werden auf $0.05 gerundet</div>
</div>
Verschiedene Rechtsgebiete haben unterschiedliche Vorschriften darüber, wer von der Rundung profitiert (Kunde oder Händler) und wie mit Sonderfällen umzugehen ist. Konsultieren Sie lokale Vorschriften für Ihren spezifischen Anwendungsfall.