avoidwork / filesize.js

JavaScript library to generate a human readable String describing the file size
https://filesizejs.com
BSD 3-Clause "New" or "Revised" License
1.61k stars 97 forks source link

E-notation for certain inputs when precision is 1 or 2 #159

Closed betamos closed 2 years ago

betamos commented 2 years ago

If the number cannot be represented with the desired precision, we get scientific ("e-") notation:

[1, 2, 3].map(p => filesize(1234567, {precision: p})) // ["1 MB", "1.2 MB", "1.23 MB"]
[1, 2, 3].map(p => filesize(12345678, {precision: p})) // ["1e+1 MB", "12 MB", "12.3 MB"]
[1, 2, 3].map(p => filesize(123456789, {precision: p})) // ["1e+2 MB", "1.2e+2 MB", "123 MB"]

I'd expect an integer even if it contains more digits than the desired precision:

[1, 2, 3].map(p => filesize(1234567, {precision: p})) // ["1 MB", "1.2 MB", "1.23 MB"]
[1, 2, 3].map(p => filesize(12345678, {precision: p})) // ["12 MB", "12 MB", "12.3 MB"]
[1, 2, 3].map(p => filesize(123456789, {precision: p})) // ["123 MB", "123 MB", "123 MB"]

My usecase for a lower precision is to reduce visual clutter in large tables. E.g. in macOS Finder, I see "816 bytes" and "2 kB" in the same column.

avoidwork commented 2 years ago

@betamos that's based on the spec for toPrecision() and not implemented by filesize() beyond executing the method on the number.

See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision#using_toprecision

// note that exponential notation might be returned in some circumstances
console.log((1234.5).toPrecision(2)) // logs '1.2e+3'
avoidwork commented 2 years ago

To clarify lets use the middle* number of 12.35 MiB with a precision of 1 becomes 10 which then becomes scientific notation because... toFixed() i guess.

I've noticed most people misunderstand what precision means; it's not the decimal points and it's base 10.

(12.3).toPrecision(1) // 10 (1e+1)
(12.3).toPrecision(2) // 12
(12.3).toPrecision(3) // 12.3

So your expectation that 10 should be 12 doesn't make sense.

arildm commented 1 year ago

Here's a workaround:

function myFilesize(bytes, opts) {
  const str = filesize(bytes, opts);
  // Convert exponential notation to ordinary.
  return str.replace(/[\d.]+e[+\d]+/, parseFloat);
}

console.log([1, 2, 3].map((p) => myFilesize(1234567, { precision: p }))); // ['1 MB', '1.2 MB', '1.23 MB']
console.log([1, 2, 3].map((p) => myFilesize(12345678, { precision: p }))); // ['10 MB', '12 MB', '12.3 MB']
console.log([1, 2, 3].map((p) => myFilesize(123456789, { precision: p }))); // ['100 MB', '120 MB', '123 MB']
avoidwork commented 1 year ago

This is what you're working around: https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.prototype.toprecision

You could have more efficient code if you used the partial() exported function to cache your opts if you actually have code like that in irl.