Сравните десятичные строки в Javascript - PullRequest
3 голосов
/ 26 февраля 2020

У меня есть две строки, каждая из которых представляет неотрицательное рациональное число в десятичном формате.

Учитывая эти две строки x и y, я хотел бы проверить, значение цифр c, представленное x, больше значения чисел c, представленного y.

Эти два значения могут быть очень большими, а также расширяться с очень высокой точностью ( т.е. много цифр после десятичной точки).

Поэтому я не могу полагаться на Number(x) > Number(y) для выполнения этой работы.

Вместо этого я реализовал следующую функцию:

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;
}

Я провел обширное тестирование, сгенерировав случайные данные x и y и сравнив результат isLarger(x, y) со значением выражения Number(x) > Number(y).

Конечно По причинам, указанным выше, я не могу полагаться на правильность значения выражения Number(x) > Number(y) для каждого возможного ввода x и y.

Поэтому я хотел бы получить какую-то обратную связь по функция выше:

  • в нем есть какие-то предостережения?
  • Возможно, есть более простой способ добиться этого?

Ответы [ 2 ]

1 голос
/ 26 февраля 2020

У меня есть предупреждение о вашем методе тестирования. Я реализовал это, насколько я понял из вопроса и комментариев. Чтобы проверить метод теста, я ввел грубую ошибку в isLarger, полностью игнорируя часть после десятичной запятой.

Даже с такой грубой ошибкой я получил 5 сбоев в 10 000 000 прогонов, что эквивалентно 0,5 сбоям на миллион пробегов.

Я думаю, что вам нужен метод тестирования, который помещает больше случаев, когда части чисел совпадают.

Вот моя программа:

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());
}
1 голос
/ 26 февраля 2020

Я не вижу никаких предупреждений в вашем решении, но последняя строка неоправданно сложна. Вместо

return xParts.join("").localeCompare(yParts.join("")) == 1;

Вы можете просто использовать это:

return xParts.join("") > yParts.join("");

Более простое решение с некоторыми ограничениями на входные значения

Если вы можете быть абсолютно уверены, что ваш ввод никогда не будет содержать чисел с ненужными начальными нулями (например, 001 вместо 1) или десятичных чисел, начинающихся с точки (например, .1 вместо 0.1) Вы можете значительно упростить сравнение, сначала сравнив количество цифр в целочисленных частях.

Чем больше цифр в целочисленной части, тем больше число, меньше цифр означает меньшее число.

Когда количество целых чисел одинаково, простого сравнения строк в принципе достаточно. Необходима только одна операция заполнения - y необходимо дополнить конечными нулями, чтобы не получить достоверный результат, если x равен y, но содержит больше конечных нулей (потому что '211.00' > '211.0' равно 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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...