Skip to content

Commit

Permalink
Consistent SI-prefix for small numbers.
Browse files Browse the repository at this point in the history
For reasons that I can’t recall, the SI-prefix behavior was different for small
numbers (between -1 and 1) than it was for large numbers. This commit enforces
consistent behavior, so that the coefficient is always in the range [1, 1000),
like in engineering notation.

For example, the old d3.format("s") would display 0.01 as "0.01", whereas the
new behavior displays it as "10m".
  • Loading branch information
mbostock committed Mar 24, 2014
1 parent 91531cf commit 621558c
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 37 deletions.
2 changes: 1 addition & 1 deletion d3.js
Original file line number Diff line number Diff line change
Expand Up @@ -2116,7 +2116,7 @@
if (value < 0) value *= -1;
if (precision) value = d3.round(value, d3_format_precision(value, precision));
i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10);
i = Math.max(-24, Math.min(24, Math.floor((i <= 0 ? i + 1 : i - 1) / 3) * 3));
i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3));
}
return d3_formatPrefixes[8 + i / 3];
};
Expand Down
2 changes: 1 addition & 1 deletion d3.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/format/formatPrefix.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ d3.formatPrefix = function(value, precision) {
if (value < 0) value *= -1;
if (precision) value = d3.round(value, d3_format_precision(value, precision));
i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10);
i = Math.max(-24, Math.min(24, Math.floor((i <= 0 ? i + 1 : i - 1) / 3) * 3));
i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3));
}
return d3_formatPrefixes[8 + i / 3];
};
Expand Down
12 changes: 10 additions & 2 deletions test/format/format-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ suite.addBatch({
assert.strictEqual(f(145999999.999999347), "146M");
assert.strictEqual(f(1e26), "100Y");
assert.strictEqual(f(.000001), "1.00µ");
assert.strictEqual(f(.009995), "0.0100");
assert.strictEqual(f(.009995), "10.0m");
var f = format(".4s");
assert.strictEqual(f(999.5), "999.5");
assert.strictEqual(f(999500), "999.5k");
Expand All @@ -115,12 +115,20 @@ suite.addBatch({
assert.strictEqual(f(145999999.999999347), "$146M");
assert.strictEqual(f(1e26), "$100Y");
assert.strictEqual(f(.000001), "$1.00µ");
assert.strictEqual(f(.009995), "$0.0100");
assert.strictEqual(f(.009995), "$10.0m");
var f = format("$.4s");
assert.strictEqual(f(999.5), "$999.5");
assert.strictEqual(f(999500), "$999.5k");
assert.strictEqual(f(.009995), "$9.995m");
},
"SI prefix notation precision is consistent for small and large numbers": function(format) {
assert.deepEqual(
[ 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e-0, 1e1, 1e2, 1e3, 1e4, 1e5].map(format("s")),
[ '10µ', '100µ', '1m', '10m', '100m', '1', '10', '100', '1k', '10k', '100k']);
assert.deepEqual(
[ 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e-0, 1e1, 1e2, 1e3, 1e4, 1e5].map(format(".4s")),
['10.00µ', '100.0µ', '1.000m', '10.00m', '100.0m', '1.000', '10.00', '100.0', '1.000k', '10.00k', '100.0k']);
},
"can output a currency": function(format) {
var f = format("$");
assert.strictEqual(f(0), "$0");
Expand Down
56 changes: 28 additions & 28 deletions test/format/formatPrefix-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,29 @@ suite.addBatch({
"determines the appropriate prefix for small numbers": function(prefix) {
assert.equal(prefix(0).symbol, "");
assert.equal(prefix(1e-00).symbol, "");
assert.equal(prefix(1e-01).symbol, "");
assert.equal(prefix(1e-02).symbol, "");
assert.equal(prefix(1e-01).symbol, "m");
assert.equal(prefix(1e-02).symbol, "m");
assert.equal(prefix(1e-03).symbol, "m");
assert.equal(prefix(1e-04).symbol, "m");
assert.equal(prefix(1e-05).symbol, "m");
assert.equal(prefix(1e-04).symbol, "µ");
assert.equal(prefix(1e-05).symbol, "µ");
assert.equal(prefix(1e-06).symbol, "µ");
assert.equal(prefix(1e-07).symbol, "µ");
assert.equal(prefix(1e-08).symbol, "µ");
assert.equal(prefix(1e-07).symbol, "n");
assert.equal(prefix(1e-08).symbol, "n");
assert.equal(prefix(1e-09).symbol, "n");
assert.equal(prefix(1e-10).symbol, "n");
assert.equal(prefix(1e-11).symbol, "n");
assert.equal(prefix(1e-10).symbol, "p");
assert.equal(prefix(1e-11).symbol, "p");
assert.equal(prefix(1e-12).symbol, "p");
assert.equal(prefix(1e-13).symbol, "p");
assert.equal(prefix(1e-14).symbol, "p");
assert.equal(prefix(1e-13).symbol, "f");
assert.equal(prefix(1e-14).symbol, "f");
assert.equal(prefix(1e-15).symbol, "f");
assert.equal(prefix(1e-16).symbol, "f");
assert.equal(prefix(1e-17).symbol, "f");
assert.equal(prefix(1e-16).symbol, "a");
assert.equal(prefix(1e-17).symbol, "a");
assert.equal(prefix(1e-18).symbol, "a");
assert.equal(prefix(1e-19).symbol, "a");
assert.equal(prefix(1e-20).symbol, "a");
assert.equal(prefix(1e-19).symbol, "z");
assert.equal(prefix(1e-20).symbol, "z");
assert.equal(prefix(1e-21).symbol, "z");
assert.equal(prefix(1e-22).symbol, "z");
assert.equal(prefix(1e-23).symbol, "z");
assert.equal(prefix(1e-22).symbol, "y");
assert.equal(prefix(1e-23).symbol, "y");
assert.equal(prefix(1e-24).symbol, "y");
assert.equal(prefix(1e-25).symbol, "y");
assert.equal(prefix(1e-26).symbol, "y");
Expand Down Expand Up @@ -93,18 +93,18 @@ suite.addBatch({
assert.equal(prefix(-1e27).symbol, "Y");
},
"considers the effect of rounding based on precision": function(prefix) {
assert.equal(prefix(999.5000000, 4).symbol, "");
assert.equal(prefix(999.5000000, 3).symbol, "k");
assert.equal(prefix(995.0000000, 3).symbol, "");
assert.equal(prefix(995.0000000, 2).symbol, "k");
assert.equal(prefix(950.0000000, 2).symbol, "");
assert.equal(prefix(950.0000000, 1).symbol, "k");
assert.equal(prefix(0.000009995, 4).symbol, "µ");
assert.equal(prefix(0.000009995, 3).symbol, "m");
assert.equal(prefix(0.000009950, 3).symbol, "µ");
assert.equal(prefix(0.000009950, 2).symbol, "m");
assert.equal(prefix(0.000009500, 2).symbol, "µ");
assert.equal(prefix(0.000009500, 1).symbol, "m");
assert.equal(prefix(999.50000, 4).symbol, "");
assert.equal(prefix(999.50000, 3).symbol, "k");
assert.equal(prefix(995.00000, 3).symbol, "");
assert.equal(prefix(995.00000, 2).symbol, "k");
assert.equal(prefix(950.00000, 2).symbol, "");
assert.equal(prefix(950.00000, 1).symbol, "k");
assert.equal(prefix(0.0009995, 4).symbol, "µ");
assert.equal(prefix(0.0009995, 3).symbol, "m");
assert.equal(prefix(0.0009950, 3).symbol, "µ");
assert.equal(prefix(0.0009950, 2).symbol, "m");
assert.equal(prefix(0.0009500, 2).symbol, "µ");
assert.equal(prefix(0.0009500, 1).symbol, "m");
}
}
});
Expand Down
5 changes: 1 addition & 4 deletions test/scale/linear-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,10 @@ suite.addBatch({
var x = d3.scale.linear().domain([0, 1e6]);
assert.deepEqual(x.ticks(10).map(x.tickFormat(10, "s")), ["0.0M", "0.1M", "0.2M", "0.3M", "0.4M", "0.5M", "0.6M", "0.7M", "0.8M", "0.9M", "1.0M"]);
assert.deepEqual(x.ticks(10).map(x.tickFormat(10, "+$s")), ["+$0.0M", "+$0.1M", "+$0.2M", "+$0.3M", "+$0.4M", "+$0.5M", "+$0.6M", "+$0.7M", "+$0.8M", "+$0.9M", "+$1.0M"]);
assert.deepEqual(x.ticks(10).map(x.tickFormat(10, ".1s")), ["0k", "100k", "200k", "300k", "400k", "500k", "600k", "700k", "800k", "900k", "1000k"]);
var x = d3.scale.linear().domain([0, 1e5]);
assert.deepEqual(x.ticks(10).map(x.tickFormat(10, "s")), ["0k", "10k", "20k", "30k", "40k", "50k", "60k", "70k", "80k", "90k", "100k"]);
assert.deepEqual(x.ticks(10).map(x.tickFormat(10, ".-1s")), ["0.00M", "0.01M", "0.02M", "0.03M", "0.04M", "0.05M", "0.06M", "0.07M", "0.08M", "0.09M", "0.10M"]);
var x = d3.scale.linear().domain([0, 1e-4]);
assert.deepEqual(x.ticks(10).map(x.tickFormat(10, "s")), ["0.00m", "0.01m", "0.02m", "0.03m", "0.04m", "0.05m", "0.06m", "0.07m", "0.08m", "0.09m", "0.10m"]);
assert.deepEqual(x.ticks(10).map(x.tickFormat(10, ".2s")), ["0µ", "10µ", "20µ", "30µ", "40µ", "50µ", "60µ", "70µ", "80µ", "90µ", "100µ"]);
assert.deepEqual(x.ticks(10).map(x.tickFormat(10, "s")), ["0µ", "10µ", "20µ", "30µ", "40µ", "50µ", "60µ", "70µ", "80µ", "90µ", "100µ"]);
},
"if count is not specified, defaults to 10": function(d3) {
var x = d3.scale.linear();
Expand Down

0 comments on commit 621558c

Please sign in to comment.