diff --git a/README.md b/README.md
index 2259cde..6f08901 100644
--- a/README.md
+++ b/README.md
@@ -133,9 +133,9 @@ The *symbol* can be:
The *zero* (`0`) option enables zero-padding; this implicitly sets *fill* to `0` and *align* to `=`. The *width* defines the minimum field width; if not specified, then the width will be determined by the content. The *comma* (`,`) option enables the use of a group separator, such as a comma for thousands.
-Depending on the *type*, the *precision* either indicates the number of digits that follow the decimal point (types `f` and `%`), or the number of significant digits (types ``, `e`, `g`, `r`, `s` and `p`). If the precision is not specified, it defaults to 6 for all types except `` (none), which defaults to 12. Precision is ignored for integer formats (types `b`, `o`, `d`, `x`, `X` and `c`). See [precisionFixed](#precisionFixed) and [precisionRound](#precisionRound) for help picking an appropriate precision.
+Depending on the *type*, the *precision* either indicates the number of digits that follow the decimal point (types `f` and `%`), or the number of significant digits (types ``, `e`, `g`, `K`, `r`, `s` and `p`). If the precision is not specified, it defaults to 6 for all types except `` (none), which defaults to 12. Precision is ignored for integer formats (types `b`, `o`, `d`, `x`, `X` and `c`). See [precisionFixed](#precisionFixed) and [precisionRound](#precisionRound) for help picking an appropriate precision.
-The `~` option trims insignificant trailing zeros across all format types. This is most commonly used in conjunction with types `r`, `e`, `s` and `%`. For example:
+The `~` option trims insignificant trailing zeros across all format types. This is most commonly used in conjunction with types `r`, `e`, `s`, `K` and `%`. For example:
```js
d3.format("s")(1500); // "1.50000k"
@@ -149,6 +149,7 @@ The available *type* values are:
* `g` - either decimal or exponent notation, rounded to significant digits.
* `r` - decimal notation, rounded to significant digits.
* `s` - decimal notation with an [SI prefix](#locale_formatPrefix), rounded to significant digits.
+* `K` - decimal notation with a [currency prefix](#locale_formatCurrencyPrefix), rounded to significant digits.
* `%` - multiply by 100, and then decimal notation with a percent sign.
* `p` - multiply by 100, round to significant digits, and then decimal notation with a percent sign.
* `b` - binary notation, rounded to integer.
@@ -167,7 +168,7 @@ d3.format(".1")(42); // "4e+1"
d3.format(".1")(4.2); // "4"
```
-# locale.formatPrefix(specifier, value) [<>](https://github.com/d3/d3-format/blob/master/src/locale.js#L127 "Source")
+# locale.formatPrefix(specifier, value) [<>](https://github.com/d3/d3-format/blob/master/src/locale.js#L152 "Source")
Equivalent to [*locale*.format](#locale_format), except the returned function will convert values to the units of the appropriate [SI prefix](https://en.wikipedia.org/wiki/Metric_prefix#List_of_SI_prefixes) for the specified numeric reference *value* before formatting in fixed point notation. The following prefixes are supported:
@@ -199,6 +200,16 @@ f(0.0042); // "4,200µ"
This method is useful when formatting multiple numbers in the same units for easy comparison. See [precisionPrefix](#precisionPrefix) for help picking an appropriate precision, and [bl.ocks.org/9764126](http://bl.ocks.org/mbostock/9764126) for an example.
+# locale.formatCurrencyPrefix(specifier, value) [<>](https://github.com/d3/d3-format/blob/master/src/locale.js#L153 "Source")
+
+Equivalent to [*locale*.locale_formatPrefix](#locale_formatPrefix), except it uses common currency abbreviations:
+
+* `` (none) - 10⁰
+* `K` - thousands, 10³
+* `M` - millions, 10⁶
+* `B` - billions, 10⁹
+* `T` - trillions, 10¹²
+
# d3.formatSpecifier(specifier) [<>](https://github.com/d3/d3-format/blob/master/src/formatSpecifier.js "Source")
Parses the specified *specifier*, returning an object with exposed fields that correspond to the [format specification mini-language](#locale_format) and a toString method that reconstructs the specifier. For example, `formatSpecifier("s")` returns:
@@ -290,6 +301,10 @@ f(1.2e6); // "1.2M"
f(1.3e6); // "1.3M"
```
+# d3.currencyPrecisionPrefix(step, value) [<>](https://github.com/d3/d3-format/blob/master/src/currencyPrecisionPrefix.js "Source")
+
+Returns a suggested decimal precision for use with [*locale*.formatCurrencyPrefix](#locale_formatCurrencyPrefix) given the specified numeric *step* and reference *value*. This is the equivalent of [*locale*.precisionPrefix](#locale_precisionPrefix) using common currency abbreviations instead of SI prefixes.
+
# d3.precisionRound(step, max) [<>](https://github.com/d3/d3-format/blob/master/src/precisionRound.js "Source")
Returns a suggested decimal precision for format types that round to significant digits given the specified numeric *step* and *max* values. The *step* represents the minimum absolute difference between values that will be formatted, and the *max* represents the largest absolute value that will be formatted. (This assumes that the values to be formatted are also multiples of *step*.) For example, given the numbers 0.99, 1.0, and 1.01, the *step* should be 0.01, the *max* should be 1.01, and the suggested precision is 3:
@@ -331,6 +346,7 @@ Returns a *locale* object for the specified *definition* with [*locale*.format](
* `thousands` - the group separator (e.g., `","`).
* `grouping` - the array of group sizes (e.g., `[3]`), cycled as needed.
* `currency` - the currency prefix and suffix (e.g., `["$", ""]`).
+* `currencyAbbreviations` - the list of abbreviated suffixes for currency values; an array of elements for each: units, thousands, millions, billions and trillions; defaults to `["", "K", "M", "B", "T"]`. The number of elements can vary.
* `numerals` - optional; an array of ten strings to replace the numerals 0-9.
* `percent` - optional; the percent sign (defaults to `"%"`).
* `minus` - optional; the minus sign (defaults to hyphen-minus, `"-"`).
diff --git a/locale/de-DE.json b/locale/de-DE.json
index a249762..320d2b0 100644
--- a/locale/de-DE.json
+++ b/locale/de-DE.json
@@ -2,5 +2,6 @@
"decimal": ",",
"thousands": ".",
"grouping": [3],
- "currency": ["", "\u00a0€"]
+ "currency": ["", "\u00a0€"],
+ "currencyAbbreviations": ["", "", "\u00a0Mio.", "\u00a0Mrd.", "\u00a0Bio."]
}
diff --git a/locale/en-GB.json b/locale/en-GB.json
index 3d22d7a..12862af 100644
--- a/locale/en-GB.json
+++ b/locale/en-GB.json
@@ -2,5 +2,6 @@
"decimal": ".",
"thousands": ",",
"grouping": [3],
- "currency": ["£", ""]
+ "currency": ["£", ""],
+ "currencyAbbreviations": ["", "k", "m", "bn", "tn"]
}
diff --git a/locale/en-US.json b/locale/en-US.json
index f075b86..d9aa81b 100644
--- a/locale/en-US.json
+++ b/locale/en-US.json
@@ -2,5 +2,6 @@
"decimal": ".",
"thousands": ",",
"grouping": [3],
- "currency": ["$", ""]
+ "currency": ["$", ""],
+ "currencyAbbreviations": ["", "K", "M", "B", "T"]
}
diff --git a/locale/es-ES.json b/locale/es-ES.json
index a249762..2ffed3a 100644
--- a/locale/es-ES.json
+++ b/locale/es-ES.json
@@ -2,5 +2,6 @@
"decimal": ",",
"thousands": ".",
"grouping": [3],
- "currency": ["", "\u00a0€"]
+ "currency": ["", "\u00a0€"],
+ "currencyAbbreviations": ["", "\u00a0mil", "\u00a0M", "\u00a0mil M", "\u00a0B"]
}
diff --git a/locale/fr-FR.json b/locale/fr-FR.json
index e0cf89d..151c02d 100644
--- a/locale/fr-FR.json
+++ b/locale/fr-FR.json
@@ -3,5 +3,6 @@
"thousands": "\u00a0",
"grouping": [3],
"currency": ["", "\u00a0€"],
- "percent": "\u202f%"
+ "percent": "\u202f%",
+ "currencyAbbreviations": ["", "\u00a0k", "\u00a0M", "\u00a0Md", "\u00a0Bn"]
}
diff --git a/locale/it-IT.json b/locale/it-IT.json
index 564ed46..202322a 100644
--- a/locale/it-IT.json
+++ b/locale/it-IT.json
@@ -2,5 +2,6 @@
"decimal": ",",
"thousands": ".",
"grouping": [3],
- "currency": ["€", ""]
+ "currency": ["€", ""],
+ "currencyAbbreviations": ["", "", "\u00a0Mio", "\u00a0Mrd", "\u00a0Bln"]
}
diff --git a/locale/nl-NL.json b/locale/nl-NL.json
index 7176b37..78ab0ab 100644
--- a/locale/nl-NL.json
+++ b/locale/nl-NL.json
@@ -2,5 +2,6 @@
"decimal": ",",
"thousands": ".",
"grouping": [3],
- "currency": ["€\u00a0", ""]
+ "currency": ["€\u00a0", ""],
+ "currencyAbbreviations": ["", "K", "\u00a0mln.", "\u00a0mld.", "\u00a0bln."]
}
diff --git a/src/currencyPrecisionPrefix.js b/src/currencyPrecisionPrefix.js
new file mode 100644
index 0000000..e4cb9c5
--- /dev/null
+++ b/src/currencyPrecisionPrefix.js
@@ -0,0 +1,3 @@
+import { createPrecisionPrefix } from "./precisionPrefix.js";
+
+export default createPrecisionPrefix(0, 4);
diff --git a/src/defaultLocale.js b/src/defaultLocale.js
index 9ecf0fa..200725b 100644
--- a/src/defaultLocale.js
+++ b/src/defaultLocale.js
@@ -2,6 +2,7 @@ import formatLocale from "./locale.js";
var locale;
export var format;
+export var formatCurrencyPrefix;
export var formatPrefix;
defaultLocale({
@@ -15,6 +16,7 @@ defaultLocale({
export default function defaultLocale(definition) {
locale = formatLocale(definition);
format = locale.format;
+ formatCurrencyPrefix = locale.formatCurrencyPrefix;
formatPrefix = locale.formatPrefix;
return locale;
}
diff --git a/src/formatBinaryPrefixAuto.js b/src/formatBinaryPrefixAuto.js
new file mode 100644
index 0000000..f835bdf
--- /dev/null
+++ b/src/formatBinaryPrefixAuto.js
@@ -0,0 +1,46 @@
+export var binaryPrefixExponent;
+
+export default function(x, p) {
+ var binaryExponent = 0;
+ if (x === Infinity) return binaryPrefixExponent = 0, x;
+
+ while (Math.round(x) >= 1024 && binaryExponent < 80) {
+ binaryExponent += 10;
+ x /= 1024;
+ }
+
+ if (p <= 3 && Math.round(x) >= 1000) {
+ // Unlike SI prefixes, integers can take three digits.
+ binaryExponent += 10;
+ x /= 1024;
+ }
+
+ binaryPrefixExponent = Math.max(0, Math.min(8, Math.floor(binaryExponent / 10))) * 10;
+ var i = binaryExponent - binaryPrefixExponent + 1,
+ coefficient = x * i,
+ split = ('' + coefficient).split('.'),
+ integer = split[0],
+ fraction = split[1] || '',
+ n = (integer + fraction).length;
+
+ if (n === p) return coefficient;
+
+ if (n > p) {
+ var fractionLength = Math.max(0, p - integer.length);
+
+ while (+coefficient.toFixed(fractionLength) === 0) {
+ fractionLength += 1;
+ }
+
+ coefficient = coefficient.toFixed(fractionLength);
+ } else {
+ coefficient = integer + '.' + fraction;
+
+ while (n < p) {
+ coefficient += '0';
+ n += 1;
+ }
+ }
+
+ return coefficient;
+}
diff --git a/src/formatPrefixAuto.js b/src/formatPrefixAuto.js
index c7ef7be..d11bce8 100644
--- a/src/formatPrefixAuto.js
+++ b/src/formatPrefixAuto.js
@@ -2,15 +2,25 @@ import formatDecimal from "./formatDecimal.js";
export var prefixExponent;
-export default function(x, p) {
+function formatSignificantDigitsForPrefixes(x, p, minPrefixOrder, maxPrefixOrder) {
var d = formatDecimal(x, p);
if (!d) return x + "";
var coefficient = d[0],
exponent = d[1],
- i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
+ i = exponent - (prefixExponent = Math.max(minPrefixOrder, Math.min(maxPrefixOrder, Math.floor(exponent / 3))) * 3) + 1,
n = coefficient.length;
return i === n ? coefficient
: i > n ? coefficient + new Array(i - n + 1).join("0")
: i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
- : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!
+ : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than the smallest prefix
+}
+
+export function createFormatCurrencyPrefixAutoForLocale(currencyAbbreviations) {
+ return function formatCurrencyPrefixAuto(x, p) {
+ return formatSignificantDigitsForPrefixes(x, p, 0, currencyAbbreviations.length - 1);
+ }
+}
+
+export default function(x, p) {
+ return formatSignificantDigitsForPrefixes(x, p, -8, 8);
}
diff --git a/src/formatTypes.js b/src/formatTypes.js
index cc7421d..a37d03e 100644
--- a/src/formatTypes.js
+++ b/src/formatTypes.js
@@ -1,14 +1,17 @@
-import formatPrefixAuto from "./formatPrefixAuto.js";
+import formatBinaryPrefixAuto from "./formatBinaryPrefixAuto.js";
+import formatPrefixAuto, { createFormatCurrencyPrefixAutoForLocale } from "./formatPrefixAuto.js";
import formatRounded from "./formatRounded.js";
export default {
"%": function(x, p) { return (x * 100).toFixed(p); },
+ "B": formatBinaryPrefixAuto,
"b": function(x) { return Math.round(x).toString(2); },
"c": function(x) { return x + ""; },
"d": function(x) { return Math.round(x).toString(10); },
"e": function(x, p) { return x.toExponential(p); },
"f": function(x, p) { return x.toFixed(p); },
"g": function(x, p) { return x.toPrecision(p); },
+ "K": createFormatCurrencyPrefixAutoForLocale, // depends of the current locale
"o": function(x) { return Math.round(x).toString(8); },
"p": function(x, p) { return formatRounded(x * 100, p); },
"r": formatRounded,
diff --git a/src/index.js b/src/index.js
index 22ae6b2..61cda03 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,4 +1,5 @@
-export {default as formatDefaultLocale, format, formatPrefix} from "./defaultLocale.js";
+export {default as currencyPrecisionPrefix} from "./currencyPrecisionPrefix.js";
+export {default as formatDefaultLocale, format, formatCurrencyPrefix, formatPrefix} from "./defaultLocale.js";
export {default as formatLocale} from "./locale.js";
export {default as formatSpecifier, FormatSpecifier} from "./formatSpecifier.js";
export {default as precisionFixed} from "./precisionFixed.js";
diff --git a/src/locale.js b/src/locale.js
index a8ea919..b023d0d 100644
--- a/src/locale.js
+++ b/src/locale.js
@@ -5,13 +5,17 @@ import formatSpecifier from "./formatSpecifier.js";
import formatTrim from "./formatTrim.js";
import formatTypes from "./formatTypes.js";
import {prefixExponent} from "./formatPrefixAuto.js";
+import {binaryPrefixExponent} from "./formatBinaryPrefixAuto.js";
import identity from "./identity.js";
var map = Array.prototype.map,
- prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];
+ SIprefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"],
+ defaultCurrencyAbbreviations = ["", "K", "M", "B", "T"],
+ binaryPrefixes = ["", "Ki","Mi","Gi","Ti","Pi","Ei","Zi","Yi"];
export default function(locale) {
var group = locale.grouping === undefined || locale.thousands === undefined ? identity : formatGroup(map.call(locale.grouping, Number), locale.thousands + ""),
+ currencyAbbreviations = locale.currencyAbbreviations === undefined ? defaultCurrencyAbbreviations : locale.currencyAbbreviations,
currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
decimal = locale.decimal === undefined ? "." : locale.decimal + "",
@@ -52,14 +56,18 @@ export default function(locale) {
// Is this an integer type?
// Can this type generate exponential notation?
var formatType = formatTypes[type],
- maybeSuffix = /[defgprs%]/.test(type);
+ maybeSuffix = /[BdefgKprs%]/.test(type);
+
+ if (type === 'K')
+ formatType = formatType(currencyAbbreviations);
// Set the default precision if not specified,
// or 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 = precision === undefined ? 6
- : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
+ // For financial type, default precision is 3 significant digits instead of 6.
+ precision = precision === undefined ? (type === "K" ? 3 : 6)
+ : /[BgKprs]/.test(type) ? Math.max(1, Math.min(21, precision))
: Math.max(0, Math.min(20, precision));
function format(value) {
@@ -87,7 +95,12 @@ export default function(locale) {
// Compute the prefix and suffix.
valuePrefix = (valueNegative ? (sign === "(" ? sign : minus) : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
- valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : "");
+ switch (type) {
+ case "s": valueSuffix = SIprefixes[8 + prefixExponent / 3] + valueSuffix; break;
+ case "K": valueSuffix = currencyAbbreviations[prefixExponent / 3] + valueSuffix; break;
+ case "B": valueSuffix = binaryPrefixes[binaryPrefixExponent / 10] + valueSuffix; break;
+ }
+ valueSuffix += valueNegative && sign === "(" ? ")" : "";
// Break the formatted value into the integer “value” part that can be
// grouped, and fractional or exponential “suffix” part that is not.
@@ -131,18 +144,24 @@ export default function(locale) {
return format;
}
- function formatPrefix(specifier, value) {
- var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
- e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
+ function createFormatPrefix(prefixes, minimumPrefixOrder, maximumPrefixOrder) {
+ return function(specifier, value) {
+ var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
+ e = Math.max(minimumPrefixOrder, Math.min(maximumPrefixOrder, Math.floor(exponent(value) / 3))) * 3,
k = Math.pow(10, -e),
- prefix = prefixes[8 + e / 3];
- return function(value) {
- return f(k * value) + prefix;
- };
+ prefix = prefixes[(-1 * minimumPrefixOrder) + e / 3];
+ return function (value) {
+ return f(k * value) + prefix;
+ };
+ }
}
+ var formatPrefix = createFormatPrefix(SIprefixes, -8, 8);
+ var formatCurrencyPrefix = createFormatPrefix(currencyAbbreviations, 0, currencyAbbreviations.length - 1);
+
return {
format: newFormat,
+ formatCurrencyPrefix: formatCurrencyPrefix,
formatPrefix: formatPrefix
};
}
diff --git a/src/precisionPrefix.js b/src/precisionPrefix.js
index fd6af84..3e2e28e 100644
--- a/src/precisionPrefix.js
+++ b/src/precisionPrefix.js
@@ -1,5 +1,9 @@
import exponent from "./exponent.js";
-export default function(step, value) {
- return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
+export function createPrecisionPrefix(minimumPrefixOrder, maximumPrefixOrder) {
+ return function (step, value) {
+ return Math.max(0, Math.max(minimumPrefixOrder, Math.min(maximumPrefixOrder, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
+ }
}
+
+export default createPrecisionPrefix(-8, 8);
diff --git a/test/currencyPrecisionPrefix-test.js b/test/currencyPrecisionPrefix-test.js
new file mode 100644
index 0000000..49aed8e
--- /dev/null
+++ b/test/currencyPrecisionPrefix-test.js
@@ -0,0 +1,52 @@
+var tape = require("tape"),
+ format = require("../");
+
+// For currencies, only 4 prefixes are commonly used:
+// thousands (K), millions (M), billions (B) and trillions (T)
+
+tape("precisionPrefix(step, value) returns zero between 1 and 100 (unit step)", function (test) {
+ test.equal(format.currencyPrecisionPrefix(1e+0, 1e+0), 0); // 1
+ test.equal(format.currencyPrecisionPrefix(1e+0, 1e+1), 0); // 10
+ test.equal(format.currencyPrecisionPrefix(1e+0, 1e+2), 0); // 100
+ test.end()
+});
+
+tape("precisionPrefix(step, value) returns zero between 1 and 100 (thousand step)", function (test) {
+ test.equal(format.currencyPrecisionPrefix(1e+3, 1e+3), 0); // 1K
+ test.equal(format.currencyPrecisionPrefix(1e+3, 1e+4), 0); // 10K
+ test.equal(format.currencyPrecisionPrefix(1e+3, 1e+5), 0); // 100K
+ test.end()
+});
+
+tape("precisionPrefix(step, value) returns zero between 1 and 100 (million step)", function (test) {
+ test.equal(format.currencyPrecisionPrefix(1e+6, 1e+6), 0); // 1M
+ test.equal(format.currencyPrecisionPrefix(1e+6, 1e+7), 0); // 10M
+ test.equal(format.currencyPrecisionPrefix(1e+6, 1e+8), 0); // 100M
+ test.end()
+});
+
+tape("precisionPrefix(step, value) returns zero between 1 and 100 (billion step)", function (test) {
+ test.equal(format.currencyPrecisionPrefix(1e+9, 1e+9), 0); // 1B
+ test.equal(format.currencyPrecisionPrefix(1e+9, 1e+10), 0); // 10B
+ test.equal(format.currencyPrecisionPrefix(1e+9, 1e+11), 0); // 100B
+ test.end()
+});
+
+tape("currencyPrecisionPrefix(step, value) returns the expected precision when value is greater than one trillion", function(test) {
+ test.equal(format.currencyPrecisionPrefix(1e+12, 1e+12), 0); // 1T
+ test.equal(format.currencyPrecisionPrefix(1e+12, 1e+13), 0); // 10T
+ test.equal(format.currencyPrecisionPrefix(1e+12, 1e+14), 0); // 100T
+ test.equal(format.currencyPrecisionPrefix(1e+12, 1e+15), 0); // 1000T
+ test.equal(format.currencyPrecisionPrefix(1e+11, 1e+15), 1); // 1000.0T
+ test.end();
+});
+
+tape("currencyPrecisionPrefix(step, value) returns the expected precision when value is less than one unit", function(test) {
+ test.equal(format.currencyPrecisionPrefix(1e+0, 1e+0), 0); // 1
+ test.equal(format.currencyPrecisionPrefix(1e-1, 1e-1), 1); // 0.1
+ test.equal(format.currencyPrecisionPrefix(1e-2, 1e-2), 2); // 0.01
+ test.equal(format.currencyPrecisionPrefix(1e-3, 1e-3), 3); // 0.001
+ test.equal(format.currencyPrecisionPrefix(1e-4, 1e-4), 4); // 0.0001
+ test.end();
+});
+
diff --git a/test/format-type-K-test.js b/test/format-type-K-test.js
new file mode 100644
index 0000000..ed5c1ab
--- /dev/null
+++ b/test/format-type-K-test.js
@@ -0,0 +1,91 @@
+var tape = require("tape"),
+ format = require("../dist/d3-format");
+
+tape("format(\"K\") outputs currency prefix notation with default 3 significant digits", function(test) {
+ var f = format.format("K");
+ test.equal(f(0), "0.00");
+ test.equal(f(0.2), "0.20");
+ test.equal(f(1), "1.00");
+ test.equal(f(10), "10.0");
+ test.equal(f(100), "100");
+ test.equal(f(1000), "1.00K");
+ test.end();
+});
+
+tape("format(\"[.precision]K\") outputs currency-prefix notation with precision significant digits", function(test) {
+ var f1 = format.format(".2K");
+ test.equal(f1(0), "0.0");
+ test.equal(f1(1), "1.0");
+ test.equal(f1(10), "10");
+ test.equal(f1(100), "100");
+ test.equal(f1(999.5), "1.0K");
+ test.equal(f1(999500), "1.0M");
+ test.equal(f1(1000), "1.0K");
+ test.equal(f1(1500.5), "1.5K");
+ test.equal(f1(145500000), "150M");
+ test.equal(f1(145999999.999999347), "150M");
+ test.equal(f1(1e17), "100000T");
+ test.equal(f1(.000001), "0.000001");
+ var f2 = format.format(".4K");
+ test.equal(f2(999.5), "999.5");
+ test.equal(f2(999500), "999.5K");
+ test.end();
+});
+
+tape("format(\"K\") formats numbers smaller than 1", function(test) {
+ var f = format.format(".2K");
+ test.equal(f(1.29e-2), "0.0129");
+ test.equal(f(1.29e-3), "0.00129");
+ test.equal(f(-1.29e-2), "-0.0129");
+ test.equal(f(-1.29e-3), "-0.00129");
+ test.end();
+});
+
+tape("format(\"K\") formats numbers larger than thousands of trillions", function(test) {
+ var f = format.format(".2K");
+ test.equal(f(1.23e+15), "1200T");
+ test.equal(f(1.23e+16), "12000T");
+ test.equal(f(-1.23e+15), "-1200T");
+ test.equal(f(-1.23e+16), "-12000T");
+ test.end();
+});
+
+tape("format(\"$K\") outputs currency-prefix notation with a currency symbol", function(test) {
+ var f1 = format.format("$.2K");
+ test.equal(f1(0), "$0.0");
+ test.equal(f1(2.5e5), "$250K");
+ test.equal(f1(-2.5e8), "-$250M");
+ test.equal(f1(2.5e11), "$250B");
+ var f2 = format.format("$.3K");
+ test.equal(f2(0), "$0.00");
+ test.equal(f2(1), "$1.00");
+ test.equal(f2(10), "$10.0");
+ test.equal(f2(100), "$100");
+ test.equal(f2(999.5), "$1.00K");
+ test.equal(f2(999500), "$1.00M");
+ test.equal(f2(1000), "$1.00K");
+ test.equal(f2(1500.5), "$1.50K");
+ test.equal(f2(145500000), "$146M");
+ test.equal(f2(145999999.999999347), "$146M");
+ test.equal(f2(1e18), "$1000000T");
+ test.equal(f2(.000001), "$0.000001");
+ test.equal(f2(.009995), "$0.01");
+ var f3 = format.format("$.4K");
+ test.equal(f3(999.5), "$999.5");
+ test.equal(f3(999500), "$999.5K");
+ test.equal(f3(.009995), "$0.001");
+ test.end();
+});
+
+tape("format(\"0[width],K\") will group thousands due to zero fill", function(test) {
+ var f = format.format("015,K");
+ test.equal(f(42), "0,000,000,042.0");
+ test.equal(f(42e12), "0,000,000,042.0T");
+ test.end();
+});
+
+tape("format(\",K\") will group thousands for very large numbers", function(test) {
+ var f = format.format(",K");
+ test.equal(f(42e30), "42,000,000,000,000,000,000T");
+ test.end();
+});
diff --git a/test/format-type-bi-test.js b/test/format-type-bi-test.js
new file mode 100644
index 0000000..6ac73cf
--- /dev/null
+++ b/test/format-type-bi-test.js
@@ -0,0 +1,151 @@
+var tape = require("tape"),
+ format = require("../");
+
+tape("format(\"B\") outputs binary-prefix notation with default precision 6", function(test) {
+ var f = format.format("B");
+ 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(1000), "1000.00");
+ test.equal(f(999500), "976.074Ki");
+ test.equal(f(1000000), "976.563Ki");
+ test.equal(f(100), "100.000");
+ test.equal(f(1024), "1.00000Ki");
+ test.equal(f(1280), "1.25000Ki");
+ test.equal(f(1536.512), "1.50050Ki");
+ test.equal(f(.00001), "0.00001");
+ test.equal(f(.000001), "0.000001");
+ test.end();
+});
+
+tape("format(\"[.precision]B\") outputs binary-prefix notation with precision significant digits", function(test) {
+ var f1 = format.format(".3B");
+ test.equal(f1(0), "0.00");
+ test.equal(f1(1), "1.00");
+ test.equal(f1(10), "10.0");
+ test.equal(f1(100), "100");
+ test.equal(f1(1023.5), "1.00Ki");
+ test.equal(f1(1048576), "1.00Mi");
+ test.equal(f1(1048064), "1.00Mi");
+ test.equal(f1(1040000), "0.99Mi");
+ test.equal(f1(1024), "1.00Ki");
+ test.equal(f1(1536), "1.50Ki");
+ test.equal(f1(152567808), "146Mi"); // 145.5Mi
+ test.equal(f1(152567807), "145Mi"); // 145.499999Mi
+ test.equal(f1(100 * Math.pow(2, 80)), "100Yi");
+ var f2 = format.format(".4B");
+ test.equal(f2(999.5), "999.5");
+ test.equal(f2(1000), "1000");
+ test.equal(f2(999.5 * 1024), "999.5Ki");
+ test.equal(f2(1000 * 1024), "1000Ki");
+ test.end();
+});
+
+tape("format(\"B\") formats numbers smaller than 1", function(test) {
+ var f = format.format(".8B");
+ test.equal(f(1.29e-6), "0.0000013"); // Note: rounded!
+ test.equal(f(1.29e-5), "0.0000129");
+ test.equal(f(1.29e-4), "0.0001290");
+ test.equal(f(1.29e-3), "0.0012900");
+ test.equal(f(1.29e-2), "0.0129000");
+ test.equal(f(1.29e-1), "0.1290000");
+ test.end();
+});
+
+tape("format(\"B\") formats numbers larger than 2**80 with yobi", function(test) {
+ var f = format.format(".8B");
+ test.equal(f(1.23 * Math.pow(2, 70)), "1.2300000Zi");
+ test.equal(f(12.3 * Math.pow(2, 70)), "12.300000Zi");
+ test.equal(f(123 * Math.pow(2, 70)), "123.00000Zi");
+ test.equal(f(1.23 * Math.pow(2, 80)), "1.2300000Yi");
+ test.equal(f(12.3 * Math.pow(2, 80)), "12.300000Yi");
+ test.equal(f(123 * Math.pow(2, 80)), "123.00000Yi");
+ test.equal(f(1230 * Math.pow(2, 80)), "1230.0000Yi");
+ test.equal(f(12300 * Math.pow(2, 80)), "12300.000Yi");
+ test.equal(f(123000 * Math.pow(2, 80)), "123000.00Yi");
+ test.equal(f(1230000 * Math.pow(2, 80)), "1230000.0Yi");
+ test.equal(f(1234567.89 * Math.pow(2, 80)), "1234567.9Yi");
+ test.equal(f(-1.23 * Math.pow(2, 70)), "-1.2300000Zi");
+ test.equal(f(-12.3 * Math.pow(2, 70)), "-12.300000Zi");
+ test.equal(f(-123 * Math.pow(2, 70)), "-123.00000Zi");
+ test.equal(f(-1.23 * Math.pow(2, 80)), "-1.2300000Yi");
+ test.equal(f(-12.3 * Math.pow(2, 80)), "-12.300000Yi");
+ test.equal(f(-123 * Math.pow(2, 80)), "-123.00000Yi");
+ test.equal(f(-1230 * Math.pow(2, 80)), "-1230.0000Yi");
+ test.equal(f(-12300 * Math.pow(2, 80)), "-12300.000Yi");
+ test.equal(f(-123000 * Math.pow(2, 80)), "-123000.00Yi");
+ test.equal(f(-1230000 * Math.pow(2, 80)), "-1230000.0Yi");
+ test.equal(f(-1234567.89 * Math.pow(2, 80)), "-1234567.9Yi");
+ test.end();
+});
+
+tape("format(\"$B\") outputs binary-prefix notation with a currency symbol", function(test) {
+ var f1 = format.format("$.2B");
+ test.equal(f1(0), "$0.0");
+ test.equal(f1(256000), "$250Ki");
+ test.equal(f1(-250 * Math.pow(2, 20)), "-$250Mi");
+ test.equal(f1(250 * Math.pow(2, 30)), "$250Gi");
+ var f2 = format.format("$.3B");
+ test.equal(f2(0), "$0.00");
+ test.equal(f2(1), "$1.00");
+ test.equal(f2(10), "$10.0");
+ test.equal(f2(100), "$100");
+ test.equal(f2(999.4), "$999");
+ test.equal(f2(999.5), "$0.98Ki");
+ test.equal(f2(.9995 * Math.pow(2, 10)), "$1.00Ki");
+ test.equal(f2(.9995 * Math.pow(2, 20)), "$1.00Mi");
+ test.equal(f2(1024), "$1.00Ki");
+ test.equal(f2(1535.5), "$1.50Ki");
+ test.equal(f2(152567808), "$146Mi");
+ test.equal(f2(152567807), "$145Mi");
+ test.equal(f2(100 * Math.pow(2, 80)), "$100Yi");
+ test.equal(f2(.000001), "$0.000001");
+ test.equal(f2(.009995), "$0.01");
+ var f3 = format.format("$.4B");
+ test.equal(f3(1023), "$1023");
+ test.equal(f3(1023 * Math.pow(2, 10)), "$1023Ki");
+ var f4 = format.format("$.5B");
+ test.equal(f4(1023.5), "$0.9995Ki");
+ test.equal(f4(1023.5 * Math.pow(2, 10)), "$0.9995Mi");
+ test.end();
+});
+
+tape("format(\"B\") binary-prefix notation precision is consistent for small and large numbers", function(test) {
+ var f1 = format.format(".0B");
+ test.equal(f1(1e0 * Math.pow(2, 0)), "1");
+ test.equal(f1(1e1 * Math.pow(2, 0)), "10");
+ test.equal(f1(1e2 * Math.pow(2, 0)), "100");
+ test.equal(f1(1e0 * Math.pow(2, 10)), "1Ki");
+ test.equal(f1(1e1 * Math.pow(2, 10)), "10Ki");
+ test.equal(f1(1e2 * Math.pow(2, 10)), "100Ki");
+ var f2 = format.format(".4B");
+ test.equal(f2(1e+0 * Math.pow(2, 0)), "1.000");
+ test.equal(f2(1e+1 * Math.pow(2, 0)), "10.00");
+ test.equal(f2(1e+2 * Math.pow(2, 0)), "100.0");
+ test.equal(f2(1e+0 * Math.pow(2, 10)), "1.000Ki");
+ test.equal(f2(1e+1 * Math.pow(2, 10)), "10.00Ki");
+ test.equal(f2(1e+2 * Math.pow(2, 10)), "100.0Ki");
+ test.end();
+});
+
+tape("format(\"0[width],B\") will group thousands due to zero fill", function(test) {
+ var f = format.format("020,B");
+ test.equal(f(42), "000,000,000,042.0000");
+ test.equal(f(42 * Math.pow(2, 40)), "0,000,000,042.0000Ti");
+ test.end();
+});
+
+tape("format(\",B\") will group thousands for very large numbers", function(test) {
+ var f = format.format(",B");
+ test.equal(f(42e6 * Math.pow(2, 80)), "42,000,000Yi");
+ test.end();
+});
+
+tape("format(\"B\") will not hang on Infinity", function(test) {
+ var f = format.format("B");
+ test.equal(f(Infinity), "Infinity");
+ test.equal(f(-Infinity), "-Infinity");
+ test.end();
+});
diff --git a/test/formatCurrencyPrefix-test.js b/test/formatCurrencyPrefix-test.js
new file mode 100644
index 0000000..23fa7b4
--- /dev/null
+++ b/test/formatCurrencyPrefix-test.js
@@ -0,0 +1,48 @@
+var tape = require("tape"),
+ format = require("../dist/d3-format");
+
+tape("formatCurrencyPrefix(\"K\", value)(number) formats with the thousands prefix if appropriate to the specified value", function(test) {
+ test.equal(format.formatCurrencyPrefix(",.0K", 1e3)(42000), "42K");
+ test.equal(format.formatCurrencyPrefix(",.0K", 1e3)(420000), "420K");
+ test.equal(format.formatCurrencyPrefix(",.3K", 1e3)(420), "0.420K");
+ test.end();
+});
+
+tape("formatCurrencyPrefix(\"K\", value)(number) formats with the millions prefix if appropriate to the specified value", function(test) {
+ test.equal(format.formatCurrencyPrefix(",.0K", 1e6)(42000000), "42M");
+ test.equal(format.formatCurrencyPrefix(",.0K", 1e6)(420000000), "420M");
+ test.equal(format.formatCurrencyPrefix(",.3K", 1e6)(420000), "0.420M");
+ test.end();
+});
+
+tape("formatCurrencyPrefix(\"K\", value)(number) formats with the billions prefix if appropriate to the specified value", function(test) {
+ test.equal(format.formatCurrencyPrefix(",.0K", 1e9)(42 * 1e9), "42B");
+ test.equal(format.formatCurrencyPrefix(",.0K", 1e9)(420 * 1e9), "420B");
+ test.equal(format.formatCurrencyPrefix(",.3K", 1e9)(420 * 1e6), "0.420B");
+ test.end();
+});
+
+tape("formatCurrencyPrefix(\"K\", value)(number) formats with the trillions prefix if appropriate to the specified value", function(test) {
+ test.equal(format.formatCurrencyPrefix(",.0K", 1e12)(42 * 1e12), "42T");
+ test.equal(format.formatCurrencyPrefix(",.0K", 1e12)(420 * 1e12), "420T");
+ test.equal(format.formatCurrencyPrefix(",.3K", 1e12)(420 * 1e9), "0.420T");
+ test.end();
+});
+
+
+tape("formatCurrencyPrefix(\"K\", value)(number) uses nothing for very small reference values", function(test) {
+ test.equal(format.formatCurrencyPrefix(",.3K", 1e-3)(0.1), "0.100");
+ test.end();
+});
+
+tape("formatCurrencyPrefix(\"K\", value)(number) uses trillions for very large reference values", function(test) {
+ test.equal(format.formatCurrencyPrefix(",.0K", 1e15)(1e15), "1,000T");
+ test.end();
+});
+
+tape("formatCurrencyPrefix(\"$,K\", value)(number) formats with the associated currency", function(test) {
+ var f = format.formatCurrencyPrefix(" $12,.1K", 1e9);
+ test.equal(f(-42e9), " -$42.0B");
+ test.equal(f(+4.2e9), " $4.2B");
+ test.end();
+});
diff --git a/test/locale-test.js b/test/locale-test.js
index 394811d..dc3716c 100644
--- a/test/locale-test.js
+++ b/test/locale-test.js
@@ -21,6 +21,16 @@ tape("formatLocale({currency: [prefix, suffix]}) places the currency suffix afte
test.end();
});
+tape("formatLocale({currencyAbbreviations: [list of abbreviations]}) should abbreviate thousands, millions, billions and trillions", function (test) {
+ test.equal(d3.formatLocale({ currencyAbbreviations: ["", "\u00a0k", "\u00a0M", "\u00a0Md", "\u00a0Bn"] }).format("$.3K")(1.2e9), "1.20\u00a0Md");
+ test.end();
+});
+
+tape("formatLocale({currencyAbbreviations: [list of abbreviations]}) should abbreviate only specified levels", function (test) {
+ test.equal(d3.formatLocale({ currencyAbbreviations: ["", "\u00a0k", "\u00a0M", "\u00a0Md", "\u00a0Bn"] }).format("$.3K")(1.2e15), "1200\u00a0Bn");
+ test.end();
+});
+
tape("formatLocale({grouping: undefined}) does not perform any grouping", function(test) {
test.equal(d3.formatLocale({decimal: "."}).format("012,.2f")(2), "000000002.00");
test.end();