Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change which notation d3.format uses for the 'g' type

Tags:

We want to format a number so that the resulting string has length of X (in our case it's a length of 3). We're OK with exponent notation if the number is too low or too big, however we prefer to display a number without it.

That's exactly what g notation is for https://github.com/d3/d3-format

However, d3 somehow decides when the outputting number is in e notation or not. If you put more than 5 zeros in front of the first non zero number, it chooses e notation. We'd like to change this to be lower number.

d3.format(".2g")(0.0000020852945934254138)
>> "0.0000021"
d3.format(".2g")(0.00000020852945934254138)
>> "2.1e-7"

Is there any way how to modify this behaviour so that we can get a string no longer than a certain length?

like image 647
Jan Vorcak Avatar asked Jul 03 '18 15:07

Jan Vorcak


1 Answers

The decision in this case does not depend on D3.

D3 is, obviously, a JavaScript library. Internally, d3.format uses Number.prototype.toPrecision(). We can see this in the source code:

export default {
    //...
    "g": function(x, p) { return x.toPrecision(p); },
    //...
}

So, for describing the behaviour (that is, how the choice is made), we have to look at the ECMAScript documentation for Number.prototype.toPrecision(). We can find it at §20.1.3.5. The most relevant part for this question is that one at subsection 12.c, pay attention to the -6 there:

20.1.3.5 Number.prototype.toPrecision ( precision )

Return a String containing this Number value represented either in decimal exponential notation with one digit before the significand's decimal point and precision–1 digits after the significand's decimal point or in decimal fixed notation with precision significant digits. If precision is undefined, call ToString (7.1.12) instead. Specifically, perform the following steps:

[...]

12.c. If e < –6 or e ≥ p, then

[...]

vii. Return the concatenation of s, m, code unit 0x0065 (LATIN SMALL LETTER E), c, and d.

As we can see, numbers smaller than 0.000001 (that is, 1e-6) will be returned using exponential notation.

Let's check it:

console.log(d3.format("g")(0.000001))
<script src="https://d3js.org/d3.v5.min.js"></script>

As we can see, 0.000001 will be logged as regular decimal notation and not as 1e-6.

Pay attention to the fact that if no precision is set the d3.format for that specifier defaults to 6 significant figures (hence 0.00000100000), and contrary of what most people thing those five zeros at the right of the 1 are significant figures.

If you want to show just the digits until the 1, let's set the precision:

console.log(d3.format(".1g")(0.000001))
<script src="https://d3js.org/d3.v5.min.js"></script>

That being clear, let's see what happens if we use 0.0000001, which is 1e-7:

console.log(d3.format("g")(0.0000001))
<script src="https://d3js.org/d3.v5.min.js"></script>

As you can see it returns 1.00000e-7 instead of 0.0000001. And the same code, using 1 as precision:

console.log(d3.format(".1g")(0.0000001))
<script src="https://d3js.org/d3.v5.min.js"></script>

Conclusion

It seems to me that you cannot change that behaviour using D3 only. Your number (0.00000020852945934254138) is smaller than 1e-6, and therefore the exponential notation is expected here. For changing that you'll have to write your own customised function.

like image 152
Gerardo Furtado Avatar answered Sep 30 '22 05:09

Gerardo Furtado