-
Notifications
You must be signed in to change notification settings - Fork 103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement binary prefixes #70
Changes from all commits
1dfc9ad
1841fbc
b43ed9e
2fb8dbd
e33a8f1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
export var binaryPrefixExponent; | ||
|
||
export default function(x, p) { | ||
var binaryExponent = 0; | ||
if (x === Infinity) return binaryPrefixExponent = 0, x; | ||
|
||
runarberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
while (Math.round(x) >= 1024 && binaryExponent < 80) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a closed form for doing this, rather than needing a loop? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also it looks like this will hang if you try to format Infinity, but I didn’t test it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn’t investigate any closed forms. I wouldn’t be surprised if one existed using the power of 2. But this loop will run at most 8 times as far as I can tell, even for Infinity. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a test that confirms that infinity does not hang indefinitely. However if someone can point out a closed form, it would probably be preferable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds right; I was too hasty reading the code. Thanks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't it be possible to use (or the IE-compatible equivalent |
||
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; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
var tape = require("tape"), | ||
format = require("../"); | ||
|
||
tape("format(\"B\") outputs binary-prefix notation with default precision 6", function(test) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great comprehensive tests! |
||
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(); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is confusing.
Why not
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Presumably because it needs to set the exported binaryPrefixExponent (note, not binaryExponent).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aaaahhhhh I see. Side effects. Thanks for pointing that out 🙏