Last active
March 10, 2025 10:14
-
-
Save c7x43t/3fb4e23c950192e07efb24c73d467a05 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Motivation: | |
var formatted = new Intl.NumberFormat('de-DE', { | |
style: 'currency', | |
currency: 'EUR', | |
}).ormat(1332123.55); | |
// This will format currency however 1 million iterations take 27.000 ms | |
// Caching the formatter is faster at 440 ms | |
var formatter = new Intl.NumberFormat('de-DE', { | |
style: 'currency', | |
currency: 'EUR', | |
}); | |
var formatted = formatter.format(1332123.55); | |
// This improvement inspired a fast formatter version which is ~6x faster ~70ms: | |
ultraFastFormat(1332123.55); | |
// This is a proof of concept and can be used as a basis for a fully fleged formatting library | |
function ultraFastFormat(num) { | |
// Handle negative numbers by working with the absolute value. | |
const isNegative = num < 0; | |
const absNum = Math.abs(num); | |
// Multiply by 100 and round to get a whole number (in cents) | |
const totalCents = Math.round(absNum * 100); | |
const fraction = totalCents % 100; | |
let integerPart = Math.floor(totalCents / 100); | |
let intStr = ""; | |
// Unroll common cases based on the magnitude of the integer part: | |
if (integerPart < 1000) { | |
// 0 to 999: single group, no separator. | |
intStr = integerPart.toString(); | |
} else if (integerPart < 1000000) { | |
// 1,000 to 999,999: exactly two groups. | |
const a = Math.floor(integerPart / 1000); | |
const b = integerPart % 1000; | |
intStr = a.toString() + "." + (b < 10 ? "00" + b : b < 100 ? "0" + b : b.toString()); | |
} else if (integerPart < 1000000000) { | |
// 1,000,000 to 999,999,999: exactly three groups. | |
const a = Math.floor(integerPart / 1000000); | |
const rem = integerPart % 1000000; | |
const b = Math.floor(rem / 1000); | |
const c = rem % 1000; | |
intStr = a.toString() + "." + | |
(b < 10 ? "00" + b : b < 100 ? "0" + b : b.toString()) + "." + | |
(c < 10 ? "00" + c : c < 100 ? "0" + c : c.toString()); | |
} else { | |
// For numbers with 4 or more groups, fall back to the generic loop. | |
const groups = []; | |
do { | |
groups.unshift(integerPart % 1000); | |
integerPart = Math.floor(integerPart / 1000); | |
} while (integerPart > 0); | |
intStr = groups[0].toString(); | |
for (let i = 1; i < groups.length; i++) { | |
const g = groups[i]; | |
intStr += "." + (g < 10 ? "00" + g : g < 100 ? "0" + g : g.toString()); | |
} | |
} | |
// Format the fractional part using a simple ternary operator. | |
const fracStr = fraction < 10 ? "0" + fraction : fraction.toString(); | |
// Prepend a minus sign if the original number was negative. | |
return (isNegative ? '-' : '') + intStr + ',' + fracStr + ' €'; | |
} | |
// Example usage: | |
console.log(ultraFastFormat(1332123.55)); // "1.332.123,55 €" | |
console.log(ultraFastFormat(-98765.4)); // "-98.765,40 €" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment