diff --git a/README.md b/README.md index caf718a..ecf6dcc 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,33 @@ Equivalent to [*locale*.format](#locale_format) on the default U.S. English loca Equivalent to [*locale*.formatPrefix](#locale_formatPrefix) on the default U.S. English locale. Use [localeFormat](#localeFormat) to specify a different locale. +# formatSpecifier(specifier) + +Parses the specified *specifier*, returning an object with exposed fields that correspond to the [format specification mini-language](#locale_format). For example, `formatSpecifier("s")` returns: + +```js +{ + "fill": " ", + "align": ">", + "sign": "-", + "symbol": "", + "zero": false, + "width": undefined, + "comma": false, + "precision": 6, + "type": "s" +} +``` + +This method is useful for understanding how format specifiers are parsed, and for deriving new specifiers. For example, you might compute an appropriate precision based on the numbers you want to format, set the precision, and then create a new format: + +```js +var s = formatSpecifier("f"); +s.precision = 2; +var f = format(s); +f(42); // "42.00"; +``` + # locale.format(specifier) Returns a new format function with the given string *specifier*. The returned function takes a number as the only argument, and returns a string representing the formatted number. The format specifier is modeled after Python 3’s [format specification mini-language](https://docs.python.org/3/library/string.html#format-specification-mini-language). The general form of a specifier is: diff --git a/index.js b/index.js index 4b3a9d2..20b4d4b 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,4 @@ +import formatSpecifier from "./src/formatSpecifier"; import locale from "./src/format-en-US"; import localeFormat from "./src/localeFormat"; @@ -5,5 +6,6 @@ export var format = locale.format; export var formatPrefix = locale.formatPrefix; export { + formatSpecifier, localeFormat }; diff --git a/src/formatSpecifier.js b/src/formatSpecifier.js new file mode 100644 index 0000000..25c2bf7 --- /dev/null +++ b/src/formatSpecifier.js @@ -0,0 +1,55 @@ +// [[fill]align][sign][symbol][0][width][,][.precision][type] +var re = /(?:(.)?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?/i; + +export default function(specifier) { + return new FormatSpecifier(specifier); +}; + +function FormatSpecifier(specifier) { + var match = re.exec(specifier), + fill = match[1] || " ", + align = match[2] || ">", + sign = match[3] || "-", + symbol = match[4] || "", + zero = !!match[5], + width = match[6] && +match[6], + comma = !!match[7], + precision = match[8] && +match[8].slice(1), + type = match[9] || ""; + + // The "n" type is an alias for ",g". + if (type === "n") comma = true, type = "g"; + + // Map invalid types to the default format. + else if (!/[%bcdefgoprsXx]/.test(type)) type = ""; + + // If zero fill is specified, padding goes after sign and before digits. + if (zero || (fill === "0" && align === "=")) zero = fill = "0", align = "="; + + // Set the default precision if not specified. + // Note that we don’t clamp the precision here to the allowed range, + // mainly because of formatPrefix treating "s" precision as fixed. + if (precision == null) precision = type ? 6 : 12; + + this.fill = fill; + this.align = align; + this.sign = sign; + this.symbol = symbol; + this.zero = zero; + this.width = width; + this.comma = comma; + this.precision = precision; + this.type = type; +} + +FormatSpecifier.prototype.toString = function() { + return this.fill + + this.align + + this.sign + + this.symbol + + (this.zero ? "0" : "") + + (this.width == null ? "" : this.width | 0) + + (this.comma ? "," : "") + + (this.precision == null ? "" : "." + (this.precision | 0)) + + this.type; +}; diff --git a/src/localeFormat.js b/src/localeFormat.js index 33f6936..c7e1ce4 100644 --- a/src/localeFormat.js +++ b/src/localeFormat.js @@ -1,14 +1,13 @@ import formatGroup from "./formatGroup"; import formatRounded from "./formatRounded"; import formatRoundedPercentage from "./formatRoundedPercentage"; +import formatSpecifier from "./formatSpecifier"; import {default as formatAutoPrefix, exponent} from "./formatAutoPrefix"; -// [[fill]align][sign][symbol][0][width][,][.precision][type] -var re = /(?:(.)?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?([a-z%])?/i, - prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"]; +var prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"]; var formatTypes = { - " ": function(x, p) { return x.toPrecision(p).replace(/(?:\.|(\.\d+?))0+(e|$)/, "$1$2"); }, + "": function(x, p) { return x.toPrecision(p).replace(/(?:\.|(\.\d+?))0+(e|$)/, "$1$2"); }, "%": function(x, p) { return (x * 100).toFixed(p); }, "b": function(x) { return x.toString(2); }, "c": function(x) { return String.fromCharCode(x); }, @@ -34,39 +33,20 @@ export default function(locale) { decimal = locale.decimal; function format(specifier) { - var match = re.exec(specifier), - fill = match[1] || " ", - align = match[2] || ">", - sign = match[3] || "-", - symbol = match[4] || "", - zero = match[5], - width = +match[6], - comma = match[7], - precision = match[8], - type = match[9]; - - // The "n" type is an alias for ",g". - if (type === "n") comma = true, type = "g"; - - // Map invalid types to the default format. - else if (!(type in formatTypes)) type = " "; - - // If zero fill is specified, padding goes after sign and before digits. - if (zero || (fill === "0" && align === "=")) zero = fill = "0", align = "="; - - // Clamp the specified precision to the supported range. - // For significant precision, it must be in [1, 21]. - // For fixed precision, it must be in [0, 20]. - if (precision) { - precision = +precision.slice(1); - precision = /[gprs]/.test(type) - ? Math.max(1, Math.min(21, precision)) - : Math.max(0, Math.min(20, precision)); - } else { - precision = type === " " ? 12 : 6; - } - - // Compute the fixed prefix and suffix. + specifier = formatSpecifier(specifier); + + var fill = specifier.fill, + align = specifier.align, + sign = specifier.sign, + symbol = specifier.symbol, + zero = specifier.zero, + width = specifier.width, + comma = specifier.comma, + precision = specifier.precision, + type = specifier.type; + + // Compute the prefix and suffix. + // For SI-prefix, the suffix is lazily computed. var prefix = symbol === "$" ? currency[0] : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "", suffix = symbol === "$" ? currency[1] : /[%p]/.test(type) ? "%" : ""; @@ -75,9 +55,16 @@ export default function(locale) { // Can this type generate exponential notation? var formatType = formatTypes[type], integer = /[bcdoxX]/.test(type), - maybeExponent = /[ deg]/.test(type), + maybeExponent = !type || /[deg]/.test(type), maybeDecimal = maybeExponent || /[fprs%]/.test(type); + // Clamp the specified precision to the supported range. + // For significant precision, it must be in [1, 21]. + // For fixed precision, it must be in [0, 20]. + precision = /[gprs]/.test(type) + ? Math.max(1, Math.min(21, precision)) + : Math.max(0, Math.min(20, precision)); + return function(value) { value = +value; @@ -133,12 +120,11 @@ export default function(locale) { } function formatPrefix(specifier, prefix) { - var match = re.exec(specifier), - prefixIndex = prefixes.indexOf(prefix), - scale = Math.pow(10, (prefixIndex < 0 ? (prefix = "", 0) : 8 - prefixIndex) * 3), - f = (match[0] = match[2] = null, match[9] = "f", format(match.join(""))); + var f = format((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)), + i = prefixes.indexOf(prefix), + k = Math.pow(10, (i < 0 ? (prefix = "", 0) : 8 - i) * 3); return function(value) { - return f(scale * value) + prefix; + return f(k * value) + prefix; }; } diff --git a/test/format-test.js b/test/format-test.js index a099cb2..8843287 100644 --- a/test/format-test.js +++ b/test/format-test.js @@ -6,637 +6,6 @@ tape("format(specifier)(number) returns a string", function(test) { test.end(); }); -tape("format(\".[precision]\") can round", function(test) { - test.equal(format.format(".1")(0.49), "0.5"); - test.equal(format.format(".2")(0.449), "0.45"); - test.equal(format.format(".3")(0.4449), "0.445"); - test.equal(format.format(".5")(0.444449), "0.44445"); - test.equal(format.format(".1")(100), "1e+2"); - test.equal(format.format(".3")(100), "100"); - test.equal(format.format(".5")(100), "100"); - test.end(); -}); - -tape("format(\"d\") can zero fill", function(test) { - var f = format.format("08d"); - test.equal(f(0), "00000000"); - test.equal(f(42), "00000042"); - test.equal(f(42000000), "42000000"); - test.equal(f(420000000), "420000000"); - test.equal(f(-4), "-0000004"); - test.equal(f(-42), "-0000042"); - test.equal(f(-4200000), "-4200000"); - test.equal(f(-42000000), "-42000000"); - test.end(); -}); - -tape("format(\"d\") can space fill", function(test) { - var f = format.format("8d"); - test.equal(f(0), " 0"); - test.equal(f(42), " 42"); - test.equal(f(42000000), "42000000"); - test.equal(f(420000000), "420000000"); - test.equal(f(-4), " -4"); - test.equal(f(-42), " -42"); - test.equal(f(-4200000), "-4200000"); - test.equal(f(-42000000), "-42000000"); - test.end(); -}); - -tape("format(\"d\") can underscore fill", function(test) { - var f = format.format("_>8d"); - test.equal(f(0), "_______0"); - test.equal(f(42), "______42"); - test.equal(f(42000000), "42000000"); - test.equal(f(420000000), "420000000"); - test.equal(f(-4), "______-4"); - test.equal(f(-42), "_____-42"); - test.equal(f(-4200000), "-4200000"); - test.equal(f(-42000000), "-42000000"); - test.end(); -}); - -tape("format(\"d\") can zero fill with sign and group", function(test) { - var f = format.format("+08,d"); - test.equal(f(0), "+0,000,000"); - test.equal(f(42), "+0,000,042"); - test.equal(f(42000000), "+42,000,000"); - test.equal(f(420000000), "+420,000,000"); - test.equal(f(-4), "-0,000,004"); - test.equal(f(-42), "-0,000,042"); - test.equal(f(-4200000), "-4,200,000"); - test.equal(f(-42000000), "-42,000,000"); - test.end(); -}); - -tape("format(\"d\") always uses zero precision", function(test) { - var f = format.format(".2d"); - test.equal(f(0), "0"); - test.equal(f(42), "42"); - test.equal(f(-4.2), ""); - test.end(); -}); - -tape("format(\"d\") returns the empty string for non-integers", function(test) { - var f = format.format("d"); - test.equal(f(4.2), ""); - test.end(); -}); - -tape("format(\"f\") can output fixed-point notation", function(test) { - test.equal(format.format(".1f")(0.49), "0.5"); - test.equal(format.format(".2f")(0.449), "0.45"); - test.equal(format.format(".3f")(0.4449), "0.445"); - test.equal(format.format(".5f")(0.444449), "0.44445"); - test.equal(format.format(".1f")(100), "100.0"); - test.equal(format.format(".2f")(100), "100.00"); - test.equal(format.format(".3f")(100), "100.000"); - test.equal(format.format(".5f")(100), "100.00000"); - test.end(); -}); - -tape("format(\"g\") can output general notation", function(test) { - test.equal(format.format(".1g")(0.049), "0.05"); - test.equal(format.format(".1g")(0.49), "0.5"); - test.equal(format.format(".2g")(0.449), "0.45"); - test.equal(format.format(".3g")(0.4449), "0.445"); - test.equal(format.format(".5g")(0.444449), "0.44445"); - test.equal(format.format(".1g")(100), "1e+2"); - test.equal(format.format(".2g")(100), "1.0e+2"); - test.equal(format.format(".3g")(100), "100"); - test.equal(format.format(".5g")(100), "100.00"); - test.equal(format.format(".5g")(100.2), "100.20"); - test.equal(format.format(".2g")(0.002), "0.0020"); - test.end(); -}); - -tape("format(\"e\") can output exponent notation", function(test) { - var f = format.format("e"); - test.equal(f(0), "0.000000e+0"); - test.equal(f(42), "4.200000e+1"); - test.equal(f(42000000), "4.200000e+7"); - test.equal(f(420000000), "4.200000e+8"); - test.equal(f(-4), "-4.000000e+0"); - test.equal(f(-42), "-4.200000e+1"); - test.equal(f(-4200000), "-4.200000e+6"); - test.equal(f(-42000000), "-4.200000e+7"); - test.equal(format.format(".0e")(42), "4e+1") - test.equal(format.format(".3e")(42), "4.200e+1") - test.end(); -}); - -tape("format(\"s\") can output SI prefix notation", function(test) { - var f = format.format("s"); - test.equal(f(0), "0.00000"); - test.equal(f(1), "1.00000"); - test.equal(f(10), "10.0000"); - test.equal(f(100), "100.000"); - test.equal(f(999.5), "999.500"); - test.equal(f(999500), "999.500k"); - test.equal(f(1000), "1.00000k"); - test.equal(f(100), "100.000"); - test.equal(f(1400), "1.40000k"); - test.equal(f(1500.5), "1.50050k"); - test.equal(f(.00001), "10.0000µ"); - test.equal(f(.000001), "1.00000µ"); - test.end(); -}); - -tape("format(\"s\") can output SI prefix notation with appropriate rounding", function(test) { - var f = format.format(".3s"); - test.equal(f(0), "0.00"); - test.equal(f(1), "1.00"); - test.equal(f(10), "10.0"); - test.equal(f(100), "100"); - test.equal(f(999.5), "1.00k"); - test.equal(f(999500), "1.00M"); - test.equal(f(1000), "1.00k"); - test.equal(f(1500.5), "1.50k"); - test.equal(f(145500000), "146M"); - test.equal(f(145999999.999999347), "146M"); - test.equal(f(1e26), "100Y"); - test.equal(f(.000001), "1.00µ"); - test.equal(f(.009995), "10.0m"); - var f = format.format(".4s"); - test.equal(f(999.5), "999.5"); - test.equal(f(999500), "999.5k"); - test.equal(f(.009995), "9.995m"); - test.end(); -}); - -tape("format(\"s\") can handle very large and very small numbers", function(test) { - var f = format.format(".8s"); - test.equal(f(1.23e-30), "0.0000012y"); - test.equal(f(1.23e-29), "0.0000123y"); - test.equal(f(1.23e-28), "0.0001230y"); - test.equal(f(1.23e-27), "0.0012300y"); - test.equal(f(1.23e-26), "0.0123000y"); - test.equal(f(1.23e-25), "0.1230000y"); - test.equal(f(1.23e-24), "1.2300000y"); - test.equal(f(1.23e-23), "12.300000y"); - test.equal(f(1.23e-22), "123.00000y"); - test.equal(f(1.23e-21), "1.2300000z"); - test.equal(f(1.23e+21), "1.2300000Z"); - test.equal(f(1.23e+22), "12.300000Z"); - test.equal(f(1.23e+23), "123.00000Z"); - test.equal(f(1.23e+24), "1.2300000Y"); - test.equal(f(1.23e+25), "12.300000Y"); - test.equal(f(1.23e+26), "123.00000Y"); - test.equal(f(1.23e+27), "1230.0000Y"); - test.equal(f(1.23e+28), "12300.000Y"); - test.equal(f(1.23e+29), "123000.00Y"); - test.equal(f(1.23e+30), "1230000.0Y"); - test.end(); -}); - -tape("format(\"$s\") can output SI prefix notation with appropriate rounding and currency symbol", function(test) { - var f = format.format("$.3s"); - test.equal(f(0), "$0.00"); - test.equal(f(1), "$1.00"); - test.equal(f(10), "$10.0"); - test.equal(f(100), "$100"); - test.equal(f(999.5), "$1.00k"); - test.equal(f(999500), "$1.00M"); - test.equal(f(1000), "$1.00k"); - test.equal(f(1500.5), "$1.50k"); - test.equal(f(145500000), "$146M"); - test.equal(f(145999999.999999347), "$146M"); - test.equal(f(1e26), "$100Y"); - test.equal(f(.000001), "$1.00µ"); - test.equal(f(.009995), "$10.0m"); - var f = format.format("$.4s"); - test.equal(f(999.5), "$999.5"); - test.equal(f(999500), "$999.5k"); - test.equal(f(.009995), "$9.995m"); - test.end(); -}); - -tape("format(\"s\") SI prefix notation precision is consistent for small and large numbers", function(test) { - test.deepEqual( - [ 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e-0, 1e1, 1e2, 1e3, 1e4, 1e5].map(format.format(".0s")), - [ '10µ', '100µ', '1m', '10m', '100m', '1', '10', '100', '1k', '10k', '100k']); - test.deepEqual( - [ 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e-0, 1e1, 1e2, 1e3, 1e4, 1e5].map(format.format(".4s")), - ['10.00µ', '100.0µ', '1.000m', '10.00m', '100.0m', '1.000', '10.00', '100.0', '1.000k', '10.00k', '100.0k']); - test.end(); -}); - -tape("format(\"$\") can output a currency", function(test) { - var f = format.format("$"); - test.equal(f(0), "$0"); - test.equal(f(.042), "$0.042"); - test.equal(f(.42), "$0.42"); - test.equal(f(4.2), "$4.2"); - test.equal(f(-.042), "-$0.042"); - test.equal(f(-.42), "-$0.42"); - test.equal(f(-4.2), "-$4.2"); - test.end(); -}); - -tape("format(\"+$,f\") can output a currency with comma-grouping and sign", function(test) { - var f = format.format("+$,.2f"); - test.equal(f(0), "+$0.00"); - test.equal(f(0.429), "+$0.43"); - test.equal(f(-0.429), "-$0.43"); - test.equal(f(-1), "-$1.00"); - test.equal(f(1e4), "+$10,000.00"); - test.end(); -}); - -tape("format(\"$s\") can output a currency with si-prefix notation", function(test) { - var f = format.format("$.2s"); - test.equal(f(0), "$0.0"); - test.equal(f(2.5e5), "$250k"); - test.equal(f(-2.5e8), "-$250M"); - test.equal(f(2.5e11), "$250G"); - test.end(); -}); - -tape("format(\"%\") can output a whole percentage", function(test) { - var f = format.format(".0%"); - test.equal(f(0), "0%"); - test.equal(f(.042), "4%"); - test.equal(f(.42), "42%"); - test.equal(f(4.2), "420%"); - test.equal(f(-.042), "-4%"); - test.equal(f(-.42), "-42%"); - test.equal(f(-4.2), "-420%"); - test.end(); -}); - -tape("format(\".%\") can output a percentage with precision", function(test) { - var f = format.format(".1%"); - test.equal(f(.234), "23.4%"); - var f = format.format(".2%"); - test.equal(f(.234), "23.40%"); - test.end(); -}); - -tape("format(\"%\") fill respects suffix", function(test) { - test.equal(format.format("020.0%")(42), "0000000000000004200%"); - test.equal(format.format("20.0%")(42), " 4200%"); - test.end(); -}); - -tape("format(\"p\") can output a percentage", function(test) { - var f = format.format("p"); - test.equal(f(.00123), "0.123000%"); - test.equal(f(.0123), "1.23000%"); - test.equal(f(.123), "12.3000%"); - test.equal(f(.234), "23.4000%"); - test.equal(f(1.23), "123.000%"); - test.equal(f(-.00123), "-0.123000%"); - test.equal(f(-.0123), "-1.23000%"); - test.equal(f(-.123), "-12.3000%"); - test.equal(f(-1.23), "-123.000%"); - test.end(); -}); - -tape("format(\"+p\") can output a percentage with rounding and sign", function(test) { - var f = format.format("+.2p"); - test.equal(f(.00123), "+0.12%"); - test.equal(f(.0123), "+1.2%"); - test.equal(f(.123), "+12%"); - test.equal(f(1.23), "+120%"); - test.equal(f(-.00123), "-0.12%"); - test.equal(f(-.0123), "-1.2%"); - test.equal(f(-.123), "-12%"); - test.equal(f(-1.23), "-120%"); - test.end(); -}); - -tape("format(\"r\") can round to significant digits", function(test) { - test.equal(format.format(".2r")(0), "0.0"); - test.equal(format.format(".1r")(0.049), "0.05"); - test.equal(format.format(".1r")(-0.049), "-0.05"); - test.equal(format.format(".1r")(0.49), "0.5"); - test.equal(format.format(".1r")(-0.49), "-0.5"); - test.equal(format.format(".2r")(0.449), "0.45"); - test.equal(format.format(".3r")(0.4449), "0.445"); - test.equal(format.format(".3r")(1.00), "1.00"); - test.equal(format.format(".3r")(0.9995), "1.00"); - test.equal(format.format(".5r")(0.444449), "0.44445"); - test.equal(format.format("r")(123.45), "123.450"); - test.equal(format.format(".1r")(123.45), "100"); - test.equal(format.format(".2r")(123.45), "120"); - test.equal(format.format(".3r")(123.45), "123"); - test.equal(format.format(".4r")(123.45), "123.5"); - test.equal(format.format(".5r")(123.45), "123.45"); - test.equal(format.format(".6r")(123.45), "123.450"); - test.equal(format.format(".1r")(.9), "0.9"); - test.equal(format.format(".1r")(.09), "0.09"); - test.equal(format.format(".1r")(.949), "0.9"); - test.equal(format.format(".1r")(.0949), "0.09"); - test.equal(format.format(".1r")(.0000000129), "0.00000001"); - test.equal(format.format(".2r")(.0000000129), "0.000000013"); - test.equal(format.format(".2r")(.00000000129), "0.0000000013"); - test.equal(format.format(".3r")(.00000000129), "0.00000000129"); - test.equal(format.format(".4r")(.00000000129), "0.000000001290"); - test.equal(format.format(".10r")(.9999999999), "0.9999999999"); - test.equal(format.format(".15r")(.999999999999999), "0.999999999999999"); - test.end(); -}); - -tape("format(\"r\") can round very small numbers", function(test) { - var f = format.format(".2r"); - test.equal(f(1e-22), "0.00000000000000000000010"); - test.end(); -}); - -tape("format(\",d\") can group thousands", function(test) { - var f = format.format(",d"); - test.equal(f(0), "0"); - test.equal(f(42), "42"); - test.equal(f(42000000), "42,000,000"); - test.equal(f(420000000), "420,000,000"); - test.equal(f(-4), "-4"); - test.equal(f(-42), "-42"); - test.equal(f(-4200000), "-4,200,000"); - test.equal(f(-42000000), "-42,000,000"); - test.equal(f(1e21), "1e+21"); - test.end(); -}); - -tape("format(\"0,d\") can group thousands and zero fill", function(test) { - test.equal(format.format("01,d")(0), "0"); - test.equal(format.format("01,d")(0), "0"); - test.equal(format.format("02,d")(0), "00"); - test.equal(format.format("03,d")(0), "000"); - test.equal(format.format("04,d")(0), "0,000"); - test.equal(format.format("05,d")(0), "0,000"); - test.equal(format.format("06,d")(0), "00,000"); - test.equal(format.format("08,d")(0), "0,000,000"); - test.equal(format.format("013,d")(0), "0,000,000,000"); - test.equal(format.format("021,d")(0), "0,000,000,000,000,000"); - test.equal(format.format("013,d")(-42000000), "-0,042,000,000"); - test.equal(format.format("012,d")(1e21), "0,000,001e+21"); - test.equal(format.format("013,d")(1e21), "0,000,001e+21"); - test.equal(format.format("014,d")(1e21), "00,000,001e+21"); - test.equal(format.format("015,d")(1e21), "000,000,001e+21"); - test.end(); -}); - -tape("format(\"0,d\") can group thousands and zero fill with overflow", function(test) { - test.equal(format.format("01,d")(1), "1"); - test.equal(format.format("01,d")(1), "1"); - test.equal(format.format("02,d")(12), "12"); - test.equal(format.format("03,d")(123), "123"); - test.equal(format.format("05,d")(12345), "12,345"); - test.equal(format.format("08,d")(12345678), "12,345,678"); - test.equal(format.format("013,d")(1234567890123), "1,234,567,890,123"); - test.end(); -}); - -tape("format(\",d\") can group thousands and space fill", function(test) { - test.equal(format.format("1,d")(0), "0"); - test.equal(format.format("1,d")(0), "0"); - test.equal(format.format("2,d")(0), " 0"); - test.equal(format.format("3,d")(0), " 0"); - test.equal(format.format("5,d")(0), " 0"); - test.equal(format.format("8,d")(0), " 0"); - test.equal(format.format("13,d")(0), " 0"); - test.equal(format.format("21,d")(0), " 0"); - test.end(); -}); - -tape("format(\",d\") can group thousands and space fill with overflow", function(test) { - test.equal(format.format("1,d")(1), "1"); - test.equal(format.format("1,d")(1), "1"); - test.equal(format.format("2,d")(12), "12"); - test.equal(format.format("3,d")(123), "123"); - test.equal(format.format("5,d")(12345), "12,345"); - test.equal(format.format("8,d")(12345678), "12,345,678"); - test.equal(format.format("13,d")(1234567890123), "1,234,567,890,123"); - test.end(); -}); - -tape("format(\",g\") can group thousands with general notation", function(test) { - var f = format.format(",.12g"); - test.equal(f(0), "0.00000000000"); - test.equal(f(42), "42.0000000000"); - test.equal(f(42000000), "42,000,000.0000"); - test.equal(f(420000000), "420,000,000.000"); - test.equal(f(-4), "-4.00000000000"); - test.equal(f(-42), "-42.0000000000"); - test.equal(f(-4200000), "-4,200,000.00000"); - test.equal(f(-42000000), "-42,000,000.0000"); - test.end(); -}); - -tape("format(\",.f\") can group thousands, space fill, and round to significant digits", function(test) { - test.equal(format.format("10,.1f")(123456.49), " 123,456.5"); - test.equal(format.format("10,.2f")(1234567.449), "1,234,567.45"); - test.equal(format.format("10,.3f")(12345678.4449), "12,345,678.445"); - test.equal(format.format("10,.5f")(123456789.444449), "123,456,789.44445"); - test.equal(format.format("10,.1f")(123456), " 123,456.0"); - test.equal(format.format("10,.2f")(1234567), "1,234,567.00"); - test.equal(format.format("10,.3f")(12345678), "12,345,678.000"); - test.equal(format.format("10,.5f")(123456789), "123,456,789.00000"); - test.end(); -}); - -tape("format(\"f\") can display integers in fixed-point notation", function(test) { - test.equal(format.format("f")(42), "42.000000"); - test.end(); -}); - -tape("format(\"d\") will not display non-integers in integer format", function(test) { - test.equal(format.format("d")(4.2), ""); - test.end(); -}); - -tape("format(\"c\") unicode character", function(test) { - test.equal(format.format("c")(9731), "☃"); - test.end(); -}); - -tape("format(\"c\") does not localize a decimal point", function(test) { - test.equal(format.localeFormat({decimal: "/"}).format("c")(46), "."); - test.end(); -}); - -tape("format(\"b\") binary", function(test) { - test.equal(format.format("b")(10), "1010"); - test.end(); -}); - -tape("format(\"#b\") binary with prefix", function(test) { - test.equal(format.format("#b")(10), "0b1010"); - test.end(); -}); - -tape("format(\"o\") octal", function(test) { - test.equal(format.format("o")(10), "12"); - test.end(); -}); - -tape("format(\"#o\") octal with prefix", function(test) { - test.equal(format.format("#o")(10), "0o12"); - test.end(); -}); - -tape("format(\"x\") hexadecimal (lowercase)", function(test) { - test.equal(format.format("x")(3735928559), "deadbeef"); - test.end(); -}); - -tape("format(\"#x\") hexadecimal (lowercase) with prefix", function(test) { - test.equal(format.format("#x")(3735928559), "0xdeadbeef"); - test.end(); -}); - -tape("format(\"X\") hexadecimal (uppercase)", function(test) { - test.equal(format.format("X")(3735928559), "DEADBEEF"); - test.end(); -}); - -tape("format(\"#X\") hexadecimal (uppercase) with prefix", function(test) { - test.equal(format.format("#X")(3735928559), "0xDEADBEEF"); - test.end(); -}); - -tape("format(\"#x\") fill respects prefix", function(test) { - test.equal(format.format("#20x")(3735928559), " 0xdeadbeef"); - test.equal(format.format("#020x")(3735928559), "0x0000000000deadbeef"); - test.end(); -}); - -tape("format(\"<\") align left", function(test) { - test.equal(format.format("<1,d")(0), "0"); - test.equal(format.format("<1,d")(0), "0"); - test.equal(format.format("<2,d")(0), "0 "); - test.equal(format.format("<3,d")(0), "0 "); - test.equal(format.format("<5,d")(0), "0 "); - test.equal(format.format("<8,d")(0), "0 "); - test.equal(format.format("<13,d")(0), "0 "); - test.equal(format.format("<21,d")(0), "0 "); - test.end(); -}); - -tape("format(\">\") align right", function(test) { - test.equal(format.format(">1,d")(0), "0"); - test.equal(format.format(">1,d")(0), "0"); - test.equal(format.format(">2,d")(0), " 0"); - test.equal(format.format(">3,d")(0), " 0"); - test.equal(format.format(">5,d")(0), " 0"); - test.equal(format.format(">8,d")(0), " 0"); - test.equal(format.format(">13,d")(0), " 0"); - test.equal(format.format(">21,d")(0), " 0"); - test.equal(format.format(">21,d")(1000), " 1,000"); - test.equal(format.format(">21,d")(1e21), " 1e+21"); - test.end(); -}); - -tape("format(\"^\") align center", function(test) { - test.equal(format.format("^1,d")(0), "0"); - test.equal(format.format("^1,d")(0), "0"); - test.equal(format.format("^2,d")(0), "0 "); - test.equal(format.format("^3,d")(0), " 0 "); - test.equal(format.format("^5,d")(0), " 0 "); - test.equal(format.format("^8,d")(0), " 0 "); - test.equal(format.format("^13,d")(0), " 0 "); - test.equal(format.format("^21,d")(0), " 0 "); - test.equal(format.format("^21,d")(1000), " 1,000 "); - test.equal(format.format("^21,d")(1e21), " 1e+21 "); - test.end(); -}); - -tape("format(\"^%\") align center puts suffix adjacent to number", function(test) { - test.equal(format.format("^21.0%")(.42), " 42% "); - test.equal(format.format("^21,.0%")(422), " 42,200% "); - test.equal(format.format("^21,.0%")(-422), " -42,200% "); - test.end(); -}); - -tape("format(\"=+,d\") pad after sign", function(test) { - test.equal(format.format("=+1,d")(0), "+0"); - test.equal(format.format("=+1,d")(0), "+0"); - test.equal(format.format("=+2,d")(0), "+0"); - test.equal(format.format("=+3,d")(0), "+ 0"); - test.equal(format.format("=+5,d")(0), "+ 0"); - test.equal(format.format("=+8,d")(0), "+ 0"); - test.equal(format.format("=+13,d")(0), "+ 0"); - test.equal(format.format("=+21,d")(0), "+ 0"); - test.equal(format.format("=+21,d")(1e21), "+ 1e+21"); - test.end(); -}); - -tape("format(\"=+$,d\") pad after sign with currency", function(test) { - test.equal(format.format("=+$1,d")(0), "+$0"); - test.equal(format.format("=+$1,d")(0), "+$0"); - test.equal(format.format("=+$2,d")(0), "+$0"); - test.equal(format.format("=+$3,d")(0), "+$0"); - test.equal(format.format("=+$5,d")(0), "+$ 0"); - test.equal(format.format("=+$8,d")(0), "+$ 0"); - test.equal(format.format("=+$13,d")(0), "+$ 0"); - test.equal(format.format("=+$21,d")(0), "+$ 0"); - test.equal(format.format("=+$21,d")(1e21), "+$ 1e+21"); - test.end(); -}); - -tape("format(\" ,d\") a space can denote positive numbers", function(test) { - test.equal(format.format(" 1,d")(-1), "-1"); - test.equal(format.format(" 1,d")(0), " 0"); - test.equal(format.format(" 2,d")(0), " 0"); - test.equal(format.format(" 3,d")(0), " 0"); - test.equal(format.format(" 5,d")(0), " 0"); - test.equal(format.format(" 8,d")(0), " 0"); - test.equal(format.format(" 13,d")(0), " 0"); - test.equal(format.format(" 21,d")(0), " 0"); - test.equal(format.format(" 21,d")(1e21), " 1e+21"); - test.end(); -}); - -tape("format(\"-,d\") explicitly only use a sign for negative numbers", function(test) { - test.equal(format.format("-1,d")(-1), "-1"); - test.equal(format.format("-1,d")(0), "0"); - test.equal(format.format("-2,d")(0), " 0"); - test.equal(format.format("-3,d")(0), " 0"); - test.equal(format.format("-5,d")(0), " 0"); - test.equal(format.format("-8,d")(0), " 0"); - test.equal(format.format("-13,d")(0), " 0"); - test.equal(format.format("-21,d")(0), " 0"); - test.end(); -}); - -tape("format(\"d\") can format negative zero", function(test) { - test.equal(format.format("1d")(-0), "-0"); - test.end(); -}); - -tape("format(\"f\") can format negative zero", function(test) { - test.equal(format.format("1f")(-0), "-0.000000"); - test.end(); -}); - -tape("format(\"n\") is an alias for \",g\"", function(test) { - var f = format.format(".12n"); - test.equal(f(0), "0.00000000000"); - test.equal(f(42), "42.0000000000"); - test.equal(f(42000000), "42,000,000.0000"); - test.equal(f(420000000), "420,000,000.000"); - test.equal(f(-4), "-4.00000000000"); - test.equal(f(-42), "-42.0000000000"); - test.equal(f(-4200000), "-4,200,000.00000"); - test.equal(f(-42000000), "-42,000,000.0000"); - test.equal(f(.0042), "0.00420000000000"); - test.equal(f(.42), "0.420000000000"); - test.equal(f(1e21), "1.00000000000e+21"); - test.end(); -}); - -tape("format(\"n\") uses zero padding", function(test) { - test.equal(format.format("01.0n")(0), "0"); - test.equal(format.format("02.0n")(0), "00"); - test.equal(format.format("03.0n")(0), "000"); - test.equal(format.format("05.0n")(0), "0,000"); - test.equal(format.format("08.0n")(0), "0,000,000"); - test.equal(format.format("013.0n")(0), "0,000,000,000"); - test.equal(format.format("021.0n")(0), "0,000,000,000,000,000"); - test.equal(format.format("013.8n")(-42000000), "-0,042,000,000"); - test.end(); -}); - tape("format(\",.\") unreasonable precision values are clamped to reasonable values", function(test) { test.equal(format.format(".30f")(0), "0.00000000000000000000"); test.equal(format.format(".0g")(1), "1"); diff --git a/test/format-type-%-test.js b/test/format-type-%-test.js new file mode 100644 index 0000000..5750c8d --- /dev/null +++ b/test/format-type-%-test.js @@ -0,0 +1,35 @@ +var tape = require("tape"), + format = require("../"); + +tape("format(\"%\") can output a whole percentage", function(test) { + var f = format.format(".0%"); + test.equal(f(0), "0%"); + test.equal(f(.042), "4%"); + test.equal(f(.42), "42%"); + test.equal(f(4.2), "420%"); + test.equal(f(-.042), "-4%"); + test.equal(f(-.42), "-42%"); + test.equal(f(-4.2), "-420%"); + test.end(); +}); + +tape("format(\".%\") can output a percentage with precision", function(test) { + var f = format.format(".1%"); + test.equal(f(.234), "23.4%"); + var f = format.format(".2%"); + test.equal(f(.234), "23.40%"); + test.end(); +}); + +tape("format(\"%\") fill respects suffix", function(test) { + test.equal(format.format("020.0%")(42), "0000000000000004200%"); + test.equal(format.format("20.0%")(42), " 4200%"); + test.end(); +}); + +tape("format(\"^%\") align center puts suffix adjacent to number", function(test) { + test.equal(format.format("^21.0%")(.42), " 42% "); + test.equal(format.format("^21,.0%")(422), " 42,200% "); + test.equal(format.format("^21,.0%")(-422), " -42,200% "); + test.end(); +}); diff --git a/test/format-type-b-test.js b/test/format-type-b-test.js new file mode 100644 index 0000000..b04b327 --- /dev/null +++ b/test/format-type-b-test.js @@ -0,0 +1,12 @@ +var tape = require("tape"), + format = require("../"); + +tape("format(\"b\") binary", function(test) { + test.equal(format.format("b")(10), "1010"); + test.end(); +}); + +tape("format(\"#b\") binary with prefix", function(test) { + test.equal(format.format("#b")(10), "0b1010"); + test.end(); +}); diff --git a/test/format-type-c-test.js b/test/format-type-c-test.js new file mode 100644 index 0000000..c483e78 --- /dev/null +++ b/test/format-type-c-test.js @@ -0,0 +1,12 @@ +var tape = require("tape"), + format = require("../"); + +tape("format(\"c\") unicode character", function(test) { + test.equal(format.format("c")(9731), "☃"); + test.end(); +}); + +tape("format(\"c\") does not localize a decimal point", function(test) { + test.equal(format.localeFormat({decimal: "/"}).format("c")(46), "."); + test.end(); +}); diff --git a/test/format-type-d-test.js b/test/format-type-d-test.js new file mode 100644 index 0000000..f7e5f84 --- /dev/null +++ b/test/format-type-d-test.js @@ -0,0 +1,236 @@ +var tape = require("tape"), + format = require("../"); + +tape("format(\"d\") will not display non-integers in integer format", function(test) { + test.equal(format.format("d")(4.2), ""); + test.end(); +}); + +tape("format(\"d\") can zero fill", function(test) { + var f = format.format("08d"); + test.equal(f(0), "00000000"); + test.equal(f(42), "00000042"); + test.equal(f(42000000), "42000000"); + test.equal(f(420000000), "420000000"); + test.equal(f(-4), "-0000004"); + test.equal(f(-42), "-0000042"); + test.equal(f(-4200000), "-4200000"); + test.equal(f(-42000000), "-42000000"); + test.end(); +}); + +tape("format(\"d\") can space fill", function(test) { + var f = format.format("8d"); + test.equal(f(0), " 0"); + test.equal(f(42), " 42"); + test.equal(f(42000000), "42000000"); + test.equal(f(420000000), "420000000"); + test.equal(f(-4), " -4"); + test.equal(f(-42), " -42"); + test.equal(f(-4200000), "-4200000"); + test.equal(f(-42000000), "-42000000"); + test.end(); +}); + +tape("format(\"d\") can underscore fill", function(test) { + var f = format.format("_>8d"); + test.equal(f(0), "_______0"); + test.equal(f(42), "______42"); + test.equal(f(42000000), "42000000"); + test.equal(f(420000000), "420000000"); + test.equal(f(-4), "______-4"); + test.equal(f(-42), "_____-42"); + test.equal(f(-4200000), "-4200000"); + test.equal(f(-42000000), "-42000000"); + test.end(); +}); + +tape("format(\"d\") can zero fill with sign and group", function(test) { + var f = format.format("+08,d"); + test.equal(f(0), "+0,000,000"); + test.equal(f(42), "+0,000,042"); + test.equal(f(42000000), "+42,000,000"); + test.equal(f(420000000), "+420,000,000"); + test.equal(f(-4), "-0,000,004"); + test.equal(f(-42), "-0,000,042"); + test.equal(f(-4200000), "-4,200,000"); + test.equal(f(-42000000), "-42,000,000"); + test.end(); +}); + +tape("format(\"d\") always uses zero precision", function(test) { + var f = format.format(".2d"); + test.equal(f(0), "0"); + test.equal(f(42), "42"); + test.equal(f(-4.2), ""); + test.end(); +}); + +tape("format(\"d\") returns the empty string for non-integers", function(test) { + var f = format.format("d"); + test.equal(f(4.2), ""); + test.end(); +}); + +tape("format(\",d\") can group thousands", function(test) { + var f = format.format(",d"); + test.equal(f(0), "0"); + test.equal(f(42), "42"); + test.equal(f(42000000), "42,000,000"); + test.equal(f(420000000), "420,000,000"); + test.equal(f(-4), "-4"); + test.equal(f(-42), "-42"); + test.equal(f(-4200000), "-4,200,000"); + test.equal(f(-42000000), "-42,000,000"); + test.equal(f(1e21), "1e+21"); + test.end(); +}); + +tape("format(\"0,d\") can group thousands and zero fill", function(test) { + test.equal(format.format("01,d")(0), "0"); + test.equal(format.format("01,d")(0), "0"); + test.equal(format.format("02,d")(0), "00"); + test.equal(format.format("03,d")(0), "000"); + test.equal(format.format("04,d")(0), "0,000"); + test.equal(format.format("05,d")(0), "0,000"); + test.equal(format.format("06,d")(0), "00,000"); + test.equal(format.format("08,d")(0), "0,000,000"); + test.equal(format.format("013,d")(0), "0,000,000,000"); + test.equal(format.format("021,d")(0), "0,000,000,000,000,000"); + test.equal(format.format("013,d")(-42000000), "-0,042,000,000"); + test.equal(format.format("012,d")(1e21), "0,000,001e+21"); + test.equal(format.format("013,d")(1e21), "0,000,001e+21"); + test.equal(format.format("014,d")(1e21), "00,000,001e+21"); + test.equal(format.format("015,d")(1e21), "000,000,001e+21"); + test.end(); +}); + +tape("format(\"0,d\") can group thousands and zero fill with overflow", function(test) { + test.equal(format.format("01,d")(1), "1"); + test.equal(format.format("01,d")(1), "1"); + test.equal(format.format("02,d")(12), "12"); + test.equal(format.format("03,d")(123), "123"); + test.equal(format.format("05,d")(12345), "12,345"); + test.equal(format.format("08,d")(12345678), "12,345,678"); + test.equal(format.format("013,d")(1234567890123), "1,234,567,890,123"); + test.end(); +}); + +tape("format(\",d\") can group thousands and space fill", function(test) { + test.equal(format.format("1,d")(0), "0"); + test.equal(format.format("1,d")(0), "0"); + test.equal(format.format("2,d")(0), " 0"); + test.equal(format.format("3,d")(0), " 0"); + test.equal(format.format("5,d")(0), " 0"); + test.equal(format.format("8,d")(0), " 0"); + test.equal(format.format("13,d")(0), " 0"); + test.equal(format.format("21,d")(0), " 0"); + test.end(); +}); + +tape("format(\",d\") can group thousands and space fill with overflow", function(test) { + test.equal(format.format("1,d")(1), "1"); + test.equal(format.format("1,d")(1), "1"); + test.equal(format.format("2,d")(12), "12"); + test.equal(format.format("3,d")(123), "123"); + test.equal(format.format("5,d")(12345), "12,345"); + test.equal(format.format("8,d")(12345678), "12,345,678"); + test.equal(format.format("13,d")(1234567890123), "1,234,567,890,123"); + test.end(); +}); + +tape("format(\"d\") align right", function(test) { + test.equal(format.format(">1,d")(0), "0"); + test.equal(format.format(">1,d")(0), "0"); + test.equal(format.format(">2,d")(0), " 0"); + test.equal(format.format(">3,d")(0), " 0"); + test.equal(format.format(">5,d")(0), " 0"); + test.equal(format.format(">8,d")(0), " 0"); + test.equal(format.format(">13,d")(0), " 0"); + test.equal(format.format(">21,d")(0), " 0"); + test.equal(format.format(">21,d")(1000), " 1,000"); + test.equal(format.format(">21,d")(1e21), " 1e+21"); + test.end(); +}); + +tape("format(\"^d\") align center", function(test) { + test.equal(format.format("^1,d")(0), "0"); + test.equal(format.format("^1,d")(0), "0"); + test.equal(format.format("^2,d")(0), "0 "); + test.equal(format.format("^3,d")(0), " 0 "); + test.equal(format.format("^5,d")(0), " 0 "); + test.equal(format.format("^8,d")(0), " 0 "); + test.equal(format.format("^13,d")(0), " 0 "); + test.equal(format.format("^21,d")(0), " 0 "); + test.equal(format.format("^21,d")(1000), " 1,000 "); + test.equal(format.format("^21,d")(1e21), " 1e+21 "); + test.end(); +}); + +tape("format(\"=+,d\") pad after sign", function(test) { + test.equal(format.format("=+1,d")(0), "+0"); + test.equal(format.format("=+1,d")(0), "+0"); + test.equal(format.format("=+2,d")(0), "+0"); + test.equal(format.format("=+3,d")(0), "+ 0"); + test.equal(format.format("=+5,d")(0), "+ 0"); + test.equal(format.format("=+8,d")(0), "+ 0"); + test.equal(format.format("=+13,d")(0), "+ 0"); + test.equal(format.format("=+21,d")(0), "+ 0"); + test.equal(format.format("=+21,d")(1e21), "+ 1e+21"); + test.end(); +}); + +tape("format(\"=+$,d\") pad after sign with currency", function(test) { + test.equal(format.format("=+$1,d")(0), "+$0"); + test.equal(format.format("=+$1,d")(0), "+$0"); + test.equal(format.format("=+$2,d")(0), "+$0"); + test.equal(format.format("=+$3,d")(0), "+$0"); + test.equal(format.format("=+$5,d")(0), "+$ 0"); + test.equal(format.format("=+$8,d")(0), "+$ 0"); + test.equal(format.format("=+$13,d")(0), "+$ 0"); + test.equal(format.format("=+$21,d")(0), "+$ 0"); + test.equal(format.format("=+$21,d")(1e21), "+$ 1e+21"); + test.end(); +}); + +tape("format(\" ,d\") a space can denote positive numbers", function(test) { + test.equal(format.format(" 1,d")(-1), "-1"); + test.equal(format.format(" 1,d")(0), " 0"); + test.equal(format.format(" 2,d")(0), " 0"); + test.equal(format.format(" 3,d")(0), " 0"); + test.equal(format.format(" 5,d")(0), " 0"); + test.equal(format.format(" 8,d")(0), " 0"); + test.equal(format.format(" 13,d")(0), " 0"); + test.equal(format.format(" 21,d")(0), " 0"); + test.equal(format.format(" 21,d")(1e21), " 1e+21"); + test.end(); +}); + +tape("format(\"-,d\") explicitly only use a sign for negative numbers", function(test) { + test.equal(format.format("-1,d")(-1), "-1"); + test.equal(format.format("-1,d")(0), "0"); + test.equal(format.format("-2,d")(0), " 0"); + test.equal(format.format("-3,d")(0), " 0"); + test.equal(format.format("-5,d")(0), " 0"); + test.equal(format.format("-8,d")(0), " 0"); + test.equal(format.format("-13,d")(0), " 0"); + test.equal(format.format("-21,d")(0), " 0"); + test.end(); +}); + +tape("format(\"d\") can format negative zero", function(test) { + test.equal(format.format("1d")(-0), "-0"); + test.end(); +}); diff --git a/test/format-type-e-test.js b/test/format-type-e-test.js new file mode 100644 index 0000000..62c50e2 --- /dev/null +++ b/test/format-type-e-test.js @@ -0,0 +1,17 @@ +var tape = require("tape"), + format = require("../"); + +tape("format(\"e\") can output exponent notation", function(test) { + var f = format.format("e"); + test.equal(f(0), "0.000000e+0"); + test.equal(f(42), "4.200000e+1"); + test.equal(f(42000000), "4.200000e+7"); + test.equal(f(420000000), "4.200000e+8"); + test.equal(f(-4), "-4.000000e+0"); + test.equal(f(-42), "-4.200000e+1"); + test.equal(f(-4200000), "-4.200000e+6"); + test.equal(f(-42000000), "-4.200000e+7"); + test.equal(format.format(".0e")(42), "4e+1") + test.equal(format.format(".3e")(42), "4.200e+1") + test.end(); +}); diff --git a/test/format-type-f-test.js b/test/format-type-f-test.js new file mode 100644 index 0000000..2ef14b5 --- /dev/null +++ b/test/format-type-f-test.js @@ -0,0 +1,46 @@ +var tape = require("tape"), + format = require("../"); + +tape("format(\"f\") can output fixed-point notation", function(test) { + test.equal(format.format(".1f")(0.49), "0.5"); + test.equal(format.format(".2f")(0.449), "0.45"); + test.equal(format.format(".3f")(0.4449), "0.445"); + test.equal(format.format(".5f")(0.444449), "0.44445"); + test.equal(format.format(".1f")(100), "100.0"); + test.equal(format.format(".2f")(100), "100.00"); + test.equal(format.format(".3f")(100), "100.000"); + test.equal(format.format(".5f")(100), "100.00000"); + test.end(); +}); + +tape("format(\"+$,f\") can output a currency with comma-grouping and sign", function(test) { + var f = format.format("+$,.2f"); + test.equal(f(0), "+$0.00"); + test.equal(f(0.429), "+$0.43"); + test.equal(f(-0.429), "-$0.43"); + test.equal(f(-1), "-$1.00"); + test.equal(f(1e4), "+$10,000.00"); + test.end(); +}); + +tape("format(\",.f\") can group thousands, space fill, and round to significant digits", function(test) { + test.equal(format.format("10,.1f")(123456.49), " 123,456.5"); + test.equal(format.format("10,.2f")(1234567.449), "1,234,567.45"); + test.equal(format.format("10,.3f")(12345678.4449), "12,345,678.445"); + test.equal(format.format("10,.5f")(123456789.444449), "123,456,789.44445"); + test.equal(format.format("10,.1f")(123456), " 123,456.0"); + test.equal(format.format("10,.2f")(1234567), "1,234,567.00"); + test.equal(format.format("10,.3f")(12345678), "12,345,678.000"); + test.equal(format.format("10,.5f")(123456789), "123,456,789.00000"); + test.end(); +}); + +tape("format(\"f\") can display integers in fixed-point notation", function(test) { + test.equal(format.format("f")(42), "42.000000"); + test.end(); +}); + +tape("format(\"f\") can format negative zero", function(test) { + test.equal(format.format("1f")(-0), "-0.000000"); + test.end(); +}); diff --git a/test/format-type-g-test.js b/test/format-type-g-test.js new file mode 100644 index 0000000..88c3263 --- /dev/null +++ b/test/format-type-g-test.js @@ -0,0 +1,30 @@ +var tape = require("tape"), + format = require("../"); + +tape("format(\"g\") can output general notation", function(test) { + test.equal(format.format(".1g")(0.049), "0.05"); + test.equal(format.format(".1g")(0.49), "0.5"); + test.equal(format.format(".2g")(0.449), "0.45"); + test.equal(format.format(".3g")(0.4449), "0.445"); + test.equal(format.format(".5g")(0.444449), "0.44445"); + test.equal(format.format(".1g")(100), "1e+2"); + test.equal(format.format(".2g")(100), "1.0e+2"); + test.equal(format.format(".3g")(100), "100"); + test.equal(format.format(".5g")(100), "100.00"); + test.equal(format.format(".5g")(100.2), "100.20"); + test.equal(format.format(".2g")(0.002), "0.0020"); + test.end(); +}); + +tape("format(\",g\") can group thousands with general notation", function(test) { + var f = format.format(",.12g"); + test.equal(f(0), "0.00000000000"); + test.equal(f(42), "42.0000000000"); + test.equal(f(42000000), "42,000,000.0000"); + test.equal(f(420000000), "420,000,000.000"); + test.equal(f(-4), "-4.00000000000"); + test.equal(f(-42), "-42.0000000000"); + test.equal(f(-4200000), "-4,200,000.00000"); + test.equal(f(-42000000), "-42,000,000.0000"); + test.end(); +}); diff --git a/test/format-type-n-test.js b/test/format-type-n-test.js new file mode 100644 index 0000000..6b4278d --- /dev/null +++ b/test/format-type-n-test.js @@ -0,0 +1,30 @@ +var tape = require("tape"), + format = require("../"); + +tape("format(\"n\") is an alias for \",g\"", function(test) { + var f = format.format(".12n"); + test.equal(f(0), "0.00000000000"); + test.equal(f(42), "42.0000000000"); + test.equal(f(42000000), "42,000,000.0000"); + test.equal(f(420000000), "420,000,000.000"); + test.equal(f(-4), "-4.00000000000"); + test.equal(f(-42), "-42.0000000000"); + test.equal(f(-4200000), "-4,200,000.00000"); + test.equal(f(-42000000), "-42,000,000.0000"); + test.equal(f(.0042), "0.00420000000000"); + test.equal(f(.42), "0.420000000000"); + test.equal(f(1e21), "1.00000000000e+21"); + test.end(); +}); + +tape("format(\"n\") uses zero padding", function(test) { + test.equal(format.format("01.0n")(0), "0"); + test.equal(format.format("02.0n")(0), "00"); + test.equal(format.format("03.0n")(0), "000"); + test.equal(format.format("05.0n")(0), "0,000"); + test.equal(format.format("08.0n")(0), "0,000,000"); + test.equal(format.format("013.0n")(0), "0,000,000,000"); + test.equal(format.format("021.0n")(0), "0,000,000,000,000,000"); + test.equal(format.format("013.8n")(-42000000), "-0,042,000,000"); + test.end(); +}); diff --git a/test/format-type-none-test.js b/test/format-type-none-test.js new file mode 100644 index 0000000..f74fef4 --- /dev/null +++ b/test/format-type-none-test.js @@ -0,0 +1,25 @@ +var tape = require("tape"), + format = require("../"); + +tape("format(\".[precision]\") can round", function(test) { + test.equal(format.format(".1")(0.49), "0.5"); + test.equal(format.format(".2")(0.449), "0.45"); + test.equal(format.format(".3")(0.4449), "0.445"); + test.equal(format.format(".5")(0.444449), "0.44445"); + test.equal(format.format(".1")(100), "1e+2"); + test.equal(format.format(".3")(100), "100"); + test.equal(format.format(".5")(100), "100"); + test.end(); +}); + +tape("format(\"$\") can output a currency", function(test) { + var f = format.format("$"); + test.equal(f(0), "$0"); + test.equal(f(.042), "$0.042"); + test.equal(f(.42), "$0.42"); + test.equal(f(4.2), "$4.2"); + test.equal(f(-.042), "-$0.042"); + test.equal(f(-.42), "-$0.42"); + test.equal(f(-4.2), "-$4.2"); + test.end(); +}); diff --git a/test/format-type-o-test.js b/test/format-type-o-test.js new file mode 100644 index 0000000..52e084f --- /dev/null +++ b/test/format-type-o-test.js @@ -0,0 +1,12 @@ +var tape = require("tape"), + format = require("../"); + +tape("format(\"o\") octal", function(test) { + test.equal(format.format("o")(10), "12"); + test.end(); +}); + +tape("format(\"#o\") octal with prefix", function(test) { + test.equal(format.format("#o")(10), "0o12"); + test.end(); +}); diff --git a/test/format-type-p-test.js b/test/format-type-p-test.js new file mode 100644 index 0000000..8c9abf8 --- /dev/null +++ b/test/format-type-p-test.js @@ -0,0 +1,29 @@ +var tape = require("tape"), + format = require("../"); + +tape("format(\"p\") can output a percentage", function(test) { + var f = format.format("p"); + test.equal(f(.00123), "0.123000%"); + test.equal(f(.0123), "1.23000%"); + test.equal(f(.123), "12.3000%"); + test.equal(f(.234), "23.4000%"); + test.equal(f(1.23), "123.000%"); + test.equal(f(-.00123), "-0.123000%"); + test.equal(f(-.0123), "-1.23000%"); + test.equal(f(-.123), "-12.3000%"); + test.equal(f(-1.23), "-123.000%"); + test.end(); +}); + +tape("format(\"+p\") can output a percentage with rounding and sign", function(test) { + var f = format.format("+.2p"); + test.equal(f(.00123), "+0.12%"); + test.equal(f(.0123), "+1.2%"); + test.equal(f(.123), "+12%"); + test.equal(f(1.23), "+120%"); + test.equal(f(-.00123), "-0.12%"); + test.equal(f(-.0123), "-1.2%"); + test.equal(f(-.123), "-12%"); + test.equal(f(-1.23), "-120%"); + test.end(); +}); diff --git a/test/format-type-r-test.js b/test/format-type-r-test.js new file mode 100644 index 0000000..7a3386e --- /dev/null +++ b/test/format-type-r-test.js @@ -0,0 +1,40 @@ +var tape = require("tape"), + format = require("../"); + +tape("format(\"r\") can round to significant digits", function(test) { + test.equal(format.format(".2r")(0), "0.0"); + test.equal(format.format(".1r")(0.049), "0.05"); + test.equal(format.format(".1r")(-0.049), "-0.05"); + test.equal(format.format(".1r")(0.49), "0.5"); + test.equal(format.format(".1r")(-0.49), "-0.5"); + test.equal(format.format(".2r")(0.449), "0.45"); + test.equal(format.format(".3r")(0.4449), "0.445"); + test.equal(format.format(".3r")(1.00), "1.00"); + test.equal(format.format(".3r")(0.9995), "1.00"); + test.equal(format.format(".5r")(0.444449), "0.44445"); + test.equal(format.format("r")(123.45), "123.450"); + test.equal(format.format(".1r")(123.45), "100"); + test.equal(format.format(".2r")(123.45), "120"); + test.equal(format.format(".3r")(123.45), "123"); + test.equal(format.format(".4r")(123.45), "123.5"); + test.equal(format.format(".5r")(123.45), "123.45"); + test.equal(format.format(".6r")(123.45), "123.450"); + test.equal(format.format(".1r")(.9), "0.9"); + test.equal(format.format(".1r")(.09), "0.09"); + test.equal(format.format(".1r")(.949), "0.9"); + test.equal(format.format(".1r")(.0949), "0.09"); + test.equal(format.format(".1r")(.0000000129), "0.00000001"); + test.equal(format.format(".2r")(.0000000129), "0.000000013"); + test.equal(format.format(".2r")(.00000000129), "0.0000000013"); + test.equal(format.format(".3r")(.00000000129), "0.00000000129"); + test.equal(format.format(".4r")(.00000000129), "0.000000001290"); + test.equal(format.format(".10r")(.9999999999), "0.9999999999"); + test.equal(format.format(".15r")(.999999999999999), "0.999999999999999"); + test.end(); +}); + +tape("format(\"r\") can round very small numbers", function(test) { + var f = format.format(".2r"); + test.equal(f(1e-22), "0.00000000000000000000010"); + test.end(); +}); diff --git a/test/format-type-s-test.js b/test/format-type-s-test.js new file mode 100644 index 0000000..fbaa2fe --- /dev/null +++ b/test/format-type-s-test.js @@ -0,0 +1,159 @@ +var tape = require("tape"), + format = require("../"); + +tape("format(\"s\") outputs SI-prefix notation with default precision 6", function(test) { + var f = format.format("s"); + test.equal(f(0), "0.00000"); + test.equal(f(1), "1.00000"); + test.equal(f(10), "10.0000"); + test.equal(f(100), "100.000"); + test.equal(f(999.5), "999.500"); + test.equal(f(999500), "999.500k"); + test.equal(f(1000), "1.00000k"); + test.equal(f(100), "100.000"); + test.equal(f(1400), "1.40000k"); + test.equal(f(1500.5), "1.50050k"); + test.equal(f(.00001), "10.0000µ"); + test.equal(f(.000001), "1.00000µ"); + test.end(); +}); + +tape("format(\"[.precision]s\") outputs SI-prefix notation with precision significant digits", function(test) { + var f = format.format(".3s"); + test.equal(f(0), "0.00"); + test.equal(f(1), "1.00"); + test.equal(f(10), "10.0"); + test.equal(f(100), "100"); + test.equal(f(999.5), "1.00k"); + test.equal(f(999500), "1.00M"); + test.equal(f(1000), "1.00k"); + test.equal(f(1500.5), "1.50k"); + test.equal(f(145500000), "146M"); + test.equal(f(145999999.999999347), "146M"); + test.equal(f(1e26), "100Y"); + test.equal(f(.000001), "1.00µ"); + test.equal(f(.009995), "10.0m"); + var f = format.format(".4s"); + test.equal(f(999.5), "999.5"); + test.equal(f(999500), "999.5k"); + test.equal(f(.009995), "9.995m"); + test.end(); +}); + +tape("format(\"s\") formats numbers smaller than 1e-24 with yocto", function(test) { + var f = format.format(".8s"); + test.equal(f(1.23e-30), "0.0000012y"); + test.equal(f(1.23e-29), "0.0000123y"); + test.equal(f(1.23e-28), "0.0001230y"); + test.equal(f(1.23e-27), "0.0012300y"); + test.equal(f(1.23e-26), "0.0123000y"); + test.equal(f(1.23e-25), "0.1230000y"); + test.equal(f(1.23e-24), "1.2300000y"); + test.equal(f(1.23e-23), "12.300000y"); + test.equal(f(1.23e-22), "123.00000y"); + test.equal(f(1.23e-21), "1.2300000z"); + test.equal(f(-1.23e-30), "-0.0000012y"); + test.equal(f(-1.23e-29), "-0.0000123y"); + test.equal(f(-1.23e-28), "-0.0001230y"); + test.equal(f(-1.23e-27), "-0.0012300y"); + test.equal(f(-1.23e-26), "-0.0123000y"); + test.equal(f(-1.23e-25), "-0.1230000y"); + test.equal(f(-1.23e-24), "-1.2300000y"); + test.equal(f(-1.23e-23), "-12.300000y"); + test.equal(f(-1.23e-22), "-123.00000y"); + test.equal(f(-1.23e-21), "-1.2300000z"); + test.end(); +}); + +tape("format(\"s\") formats numbers larger than 1e24 with yotta", function(test) { + var f = format.format(".8s"); + test.equal(f(1.23e+21), "1.2300000Z"); + test.equal(f(1.23e+22), "12.300000Z"); + test.equal(f(1.23e+23), "123.00000Z"); + test.equal(f(1.23e+24), "1.2300000Y"); + test.equal(f(1.23e+25), "12.300000Y"); + test.equal(f(1.23e+26), "123.00000Y"); + test.equal(f(1.23e+27), "1230.0000Y"); + test.equal(f(1.23e+28), "12300.000Y"); + test.equal(f(1.23e+29), "123000.00Y"); + test.equal(f(1.23e+30), "1230000.0Y"); + test.equal(f(-1.23e+21), "-1.2300000Z"); + test.equal(f(-1.23e+22), "-12.300000Z"); + test.equal(f(-1.23e+23), "-123.00000Z"); + test.equal(f(-1.23e+24), "-1.2300000Y"); + test.equal(f(-1.23e+25), "-12.300000Y"); + test.equal(f(-1.23e+26), "-123.00000Y"); + test.equal(f(-1.23e+27), "-1230.0000Y"); + test.equal(f(-1.23e+28), "-12300.000Y"); + test.equal(f(-1.23e+29), "-123000.00Y"); + test.equal(f(-1.23e+30), "-1230000.0Y"); + test.end(); +}); + +tape("format(\"$s\") outputs SI-prefix notation with a currency symbol", function(test) { + var f = format.format("$.2s"); + test.equal(f(0), "$0.0"); + test.equal(f(2.5e5), "$250k"); + test.equal(f(-2.5e8), "-$250M"); + test.equal(f(2.5e11), "$250G"); + var f = format.format("$.3s"); + test.equal(f(0), "$0.00"); + test.equal(f(1), "$1.00"); + test.equal(f(10), "$10.0"); + test.equal(f(100), "$100"); + test.equal(f(999.5), "$1.00k"); + test.equal(f(999500), "$1.00M"); + test.equal(f(1000), "$1.00k"); + test.equal(f(1500.5), "$1.50k"); + test.equal(f(145500000), "$146M"); + test.equal(f(145999999.999999347), "$146M"); + test.equal(f(1e26), "$100Y"); + test.equal(f(.000001), "$1.00µ"); + test.equal(f(.009995), "$10.0m"); + var f = format.format("$.4s"); + test.equal(f(999.5), "$999.5"); + test.equal(f(999500), "$999.5k"); + test.equal(f(.009995), "$9.995m"); + test.end(); +}); + +tape("format(\"s\") SI-prefix notation precision is consistent for small and large numbers", function(test) { + var f = format.format(".0s"); + test.equal(f(1e-5), "10µ"); + test.equal(f(1e-4), "100µ"); + test.equal(f(1e-3), "1m"); + test.equal(f(1e-2), "10m"); + test.equal(f(1e-1), "100m"); + test.equal(f(1e+0), "1"); + test.equal(f(1e+1), "10"); + test.equal(f(1e+2), "100"); + test.equal(f(1e+3), "1k"); + test.equal(f(1e+4), "10k"); + test.equal(f(1e+5), "100k"); + var f = format.format(".4s"); + test.equal(f(1e-5), "10.00µ"); + test.equal(f(1e-4), "100.0µ"); + test.equal(f(1e-3), "1.000m"); + test.equal(f(1e-2), "10.00m"); + test.equal(f(1e-1), "100.0m"); + test.equal(f(1e+0), "1.000"); + test.equal(f(1e+1), "10.00"); + test.equal(f(1e+2), "100.0"); + test.equal(f(1e+3), "1.000k"); + test.equal(f(1e+4), "10.00k"); + test.equal(f(1e+5), "100.0k"); + test.end(); +}); + +tape("format(\"0[width],s\") will group thousands due to zero fill", function(test) { + var f = format.format("020,s"); + test.equal(f(42), "000,000,000,042.0000"); + test.equal(f(42e12), "00,000,000,042.0000T"); + test.end(); +}); + +tape("format(\",s\") will group thousands for very large numbers", function(test) { + var f = format.format(",s"); + test.equal(f(42e30), "42,000,000Y"); + test.end(); +}); diff --git a/test/format-type-x-test.js b/test/format-type-x-test.js new file mode 100644 index 0000000..da1273a --- /dev/null +++ b/test/format-type-x-test.js @@ -0,0 +1,68 @@ +var tape = require("tape"), + format = require("../"); + +tape("format(\"x\") returns the empty string for non-integers", function(test) { + test.equal(format.format("x")(2.4), ""); + test.end(); +}); + +tape("format(\"x\") returns the expected hexadecimal (lowercase) string", function(test) { + test.equal(format.format("x")(0xdeadbeef), "deadbeef"); + test.end(); +}); + +tape("format(\"#x\") returns the expected hexadecimal (lowercase) string with prefix", function(test) { + test.equal(format.format("#x")(0xdeadbeef), "0xdeadbeef"); + test.end(); +}); + +tape("format(\",x\") groups thousands", function(test) { + test.equal(format.format(",x")(0xdeadbeef), "de,adb,eef"); + test.end(); +}); + +tape("format(\",x\") groups thousands", function(test) { + test.equal(format.format(",x")(0xdeadbeef), "de,adb,eef"); + test.end(); +}); + +tape("format(\"#,x\") does not group the prefix", function(test) { + test.equal(format.format("#,x")(0xadeadbeef), "0xade,adb,eef"); + test.end(); +}); + +tape("format(\"+#x\") puts the sign before the prefix", function(test) { + test.equal(format.format("+#x")(0xdeadbeef), "+0xdeadbeef"); + test.equal(format.format("+#x")(-0xdeadbeef), "-0xdeadbeef"); + test.equal(format.format(" #x")(0xdeadbeef), " 0xdeadbeef"); + test.equal(format.format(" #x")(-0xdeadbeef), "-0xdeadbeef"); + test.end(); +}); + +tape("format(\"$,x\") formats hexadecimal currency", function(test) { + test.equal(format.format("$,x")(0xdeadbeef), "$de,adb,eef"); + test.end(); +}); + +tape("format(\"[.precision]x\") always has precision zero", function(test) { + test.equal(format.format(".2x")(0xdeadbeef), "deadbeef"); + test.end(); +}); + +tape("format(\"X\") returns the expected hexadecimal (uppercase) string", function(test) { + test.equal(format.format("X")(0xdeadbeef), "DEADBEEF"); + test.end(); +}); + +tape("format(\"#X\") returns the expected hexadecimal (uppercase) string with prefix", function(test) { + test.equal(format.format("#X")(0xdeadbeef), "0xDEADBEEF"); + test.end(); +}); + +tape("format(\"#[width]x\") considers the prefix", function(test) { + test.equal(format.format("20x")(0xdeadbeef), " deadbeef"); + test.equal(format.format("#20x")(0xdeadbeef), " 0xdeadbeef"); + test.equal(format.format("020x")(0xdeadbeef), "000000000000deadbeef"); + test.equal(format.format("#020x")(0xdeadbeef), "0x0000000000deadbeef"); + test.end(); +});