Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Last active May 9, 2019 21:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dfkaye/88102758495499f40bb547592281fea6 to your computer and use it in GitHub Desktop.
Save dfkaye/88102758495499f40bb547592281fea6 to your computer and use it in GitHub Desktop.
Get the decimal fraction of a value, if coercible to a number; exponent notation supported.
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
// 14 March 2019 - first cut.
// 18 March 2019 - support expontent notation.
// 19 March 2019 - better comment style, better return style.
// 9 May 2019 - fix 'a.b' ('.b' to '0'), trailing 0's ('.490' to '.49').
// 9 May 2019 - fix Case 2 where e's value is less than the length of the
// remaining mantissa (i.e., use m.substring(e) ), and even
// better documentation.
/**
* @function fraction accepts param of any type and returns the decimal fraction
* of the value as a string, if the value is coercible to a number.
*
* If the coerced value results in null, undefined, or NaN, the function returns
* "0".
*
* Function accepts values in exponent notation, prepending 0's as necessary to
* shift decimals to the right where e is negative, or substringing decimals at
* index of e if e is positive but less than the number of decimals.
*
* Some examples:
*
* '9.123456789012345e-21' will return '.000000000000000000009123456789012345'
* '555.1111e-10' will return '.00000005551111'
* '2.222200000e2', will return '.22'
* '.123456789e8', will return '.9'
*
* @param {*} num - may be string, number, boolean, array, object...
* @returns {string} a fractional value coercible from the input.
*/
function fraction(num) {
// First, convert input to a string and remove commas or spaces (i.e., convert
// '1 234.56' to 1234.567).
var s = String(num).replace(/[\,\s]/g, '');
// Second, coerce the converted value to a number.
// If the value is NaN, assign as null.
// If it's not NaN, take the string to the right of the decimal point.
// If there isn't a decimal, split() returns null.
var m = +s === +s ? s.split('.')[1] : null;
// Third, check for exponent notation.
var e = m ? m.split('e')[1] : 0;
if (!e) {
/*
* Case 1: No exponent notation, so handle in normal course.
* If null, undefined, or NaN, return 0, else return mantissa.
*/
return m == null || m !== m ? '0' : '.' + m.replace(/[0]+$/, '');
}
if (+e >= 0) {
/*
* Case 2: An exponent notation has been found, its value is non-negative,
* so split the decimal at 'e', taking the digits to the left (before 'e').
* If e is less than the length of the remaining digits, return the substring
* of those digits at index e; otherwise return '0'.
*/
var t = m.split('e')[0];
return e >= t.length ? '0' : '.' + t.substring(e).replace(/[0]+$/, '');
}
/*
* Case 3: An exponent notation has been found, its value is negative, so
* insert 0's between the decimal and the value before 'e'.
*/
// NOTE: The tilde operator converts, e.g., -8 to +7, to create a sized array.
var a = Array(~+e).fill(0).join('');
var r = s.split('e')[0].match(/\d/g).join('');
return '.' + a + r;
}
var tests = [
1, 2, 3.4, 1234.5678,
'1,234,567.890', '1 234 567.8976',
0.0, -0.1, -2.2, 3.490000,
NaN, null, undefined, false, true,
'a', 'a.b', 'a.b.c', 'true', 'true.1',
Math, Math.round,
'12312231231231232112.2456789',
Number.POSITIVE_INFINITY,
Number.EPSILON,
".123456789e8",
".123456789e12",
2.222200000e2,
'2.222200000e2',
9.11111111015e20,
9.11111111015e21,
9.11111111015e31,
9.1234567890123456789e-21,
9.11111111015e-10,
0.00001111e-10,
555.1111e-10
];
var results = tests.map(function(test) {
return { input: String(test), result: fraction(test) };
});
console.log(JSON.stringify(results, '', 2));
/*
[
{
"input": "1",
"result": "0"
},
{
"input": "2",
"result": "0"
},
{
"input": "3.4",
"result": ".4"
},
{
"input": "1234.5678",
"result": ".5678"
},
{
"input": "1,234,567.890",
"result": ".89"
},
{
"input": "1 234 567.8976",
"result": ".8976"
},
{
"input": "0",
"result": "0"
},
{
"input": "-0.1",
"result": ".1"
},
{
"input": "-2.2",
"result": ".2"
},
{
"input": "3.49",
"result": ".49"
},
{
"input": "NaN",
"result": "0"
},
{
"input": "null",
"result": "0"
},
{
"input": "undefined",
"result": "0"
},
{
"input": "false",
"result": "0"
},
{
"input": "true",
"result": "0"
},
{
"input": "a",
"result": "0"
},
{
"input": "a.b",
"result": "0"
},
{
"input": "a.b.c",
"result": "0"
},
{
"input": "true",
"result": "0"
},
{
"input": "true.1",
"result": "0"
},
{
"input": "[object Math]",
"result": "0"
},
{
"input": "function round() {\n [native code]\n}",
"result": "0"
},
{
"input": "12312231231231232112.2456789",
"result": ".2456789"
},
{
"input": "Infinity",
"result": "0"
},
{
"input": "2.220446049250313e-16",
"result": ".0000000000000002220446049250313"
},
{
"input": ".123456789e8",
"result": ".9"
},
{
"input": ".123456789e12",
"result": "0"
},
{
"input": "222.22",
"result": ".22"
},
{
"input": "2.222200000e2",
"result": ".22"
},
{
"input": "911111111015000000000",
"result": "0"
},
{
"input": "9.11111111015e+21",
"result": "0"
},
{
"input": "9.11111111015e+31",
"result": "0"
},
{
"input": "9.123456789012345e-21",
"result": ".000000000000000000009123456789012345"
},
{
"input": "9.11111111015e-10",
"result": ".000000000911111111015"
},
{
"input": "1.111e-15",
"result": ".000000000000001111"
},
{
"input": "5.551111e-8",
"result": ".00000005551111"
}
]
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment