I have an input of two strings, each of which representing a non-negative rational number in decimal format.
Given these two strings x
and y
, I would like to check if the numeric value represented by x
is larger than the numeric value represented by y
.
These two values can be very large, as well as to extend to a very high precision (i.e., many digits after the decimal-point).
As such, I cannot rely on Number(x) > Number(y)
to do the job.
Instead, I have implemented the following function:
function isLarger(x, y) {
const xParts = x.split(".").concat("");
const yParts = y.split(".").concat("");
xParts[0] = xParts[0].padStart(yParts[0].length, "0");
yParts[0] = yParts[0].padStart(xParts[0].length, "0");
xParts[1] = xParts[1].padEnd (yParts[1].length, "0");
yParts[1] = yParts[1].padEnd (xParts[1].length, "0");
return xParts.join("").localeCompare(yParts.join("")) == 1;
}
I have conducted extensive testing, by generating random input x
and y
, and comparing the result of isLarger(x, y)
to the value of the expression Number(x) > Number(y)
.
Of course, for the reasons mentioned above, I cannot rely on the value of the expression Number(x) > Number(y)
to be correct for every possible input x
and y
.
So I would like some kind of feedback on the function above:
I don't see any caveats in your solution, but the last line is unnecessarily complex. Instead of
return xParts.join("").localeCompare(yParts.join("")) == 1;
You could simply use this:
return xParts.join("") > yParts.join("");
If you can be absolutely sure that your input will never contain numbers with unecessary leading zeros (eg. 001
instead of 1
) or decimal numbers starting with a point (eg. .1
instead of 0.1
) you can signficantly simplify the comparison by first comparing the number of digits in the integer parts.
More digits in the integer part means a greater number, less digits means a smaller number.
When the number of integer digits is the same, a simple string comparison is basically sufficient. Only one padding operation is necessary - y
needs to be padded with trailing zeros in order not to get truthy result if x
equals y
but contains more trailing zeros (because '211.00' > '211.0'
is true
).
function isLarger(x, y) {
const xIntLength = x.search(/\.|$/); /* Finds position of . or end of string */
const yIntLength = y.search(/\.|$/); /* Finds position of . or end of string */
/* Compare lengths of int part */
if (xIntLength !== yIntLength) {
return xIntLength > yIntLength;
} else {
/* Add decimal point to Y if not present and add trailing zeros
because otherwise eg. '2.00' > '2.0' would return true */
const yWithDecimalPoint = y.includes('.') ? y : y + '.';
const paddedY = yWithDecimalPoint.padEnd(x.length,'0');
return x > paddedY;
}
}
I have a caveat about your testing method. I implemented it as far as I understood from the question and comments. To test the test method, I introduced a gross error in isLarger
, completely ignoring the portion after the decimal point.
Even with such a gross error, I got 5 failures in 10,000,000 runs, equivalent to 0.5 failures per million runs.
I think you need a test method that puts in more cases where parts of the numbers match.
Here is my program:
function isLarger(x, y) {
const xParts = x.split(".").concat("");
const yParts = y.split(".").concat("");
xParts[0] = xParts[0].padStart(yParts[0].length, "0");
yParts[0] = yParts[0].padStart(xParts[0].length, "0");
xParts[1] = xParts[1].padEnd(yParts[1].length, "0");
yParts[1] = yParts[1].padEnd(xParts[1].length, "0");
xParts[1] = "";
yParts[1] = "";
return xParts.join("").localeCompare(yParts.join("")) == 1;
}
function testIt(x, y) {
const actual = isLarger(x, y);
const expected = Number(x) > Number(y);
if (actual != expected) {
console.log("x=" + x + " y=" + y + " actual " + actual + " expected "
+ expected);
}
}
function randAsString() {
return (Math.random() * 1000000).toString();
}
testIt("3.3", "3.2");
for (var i = 0; i < 10000000; i++) {
testIt(randAsString(), randAsString());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With