Какой способ является более точным?

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

double r1 = 100.0, r2 = 1000.0, r = r2 - r1;
int n = 30;
double[] position = new double[n];
for (int i = 0; i < n; i++)
{
    position[i] = r1 + (double)i / n * r;
    // position[i] = r1 + i * r / n;
}

Это о (double)int1 / int2 * double или же int1 * double / int2, Какой способ является более точным? Какой способ я должен использовать?

Обновить

Следующий код покажет разницу:

double r1 = 1000.0, r2 = 100000.0, r = r2 - r1;
int n = 300;
double[] position = new double[n];
for (int i = 0; i < n; i++)
{
    double v1 = r1 + (double)i / n * r;
    double v2 = position[i] = r1 + i * r / n;
    if (v1 != v2)
    {
        Console.WriteLine(v2 - v1);
    }
}

3 ответа

Решение

Отказ от ответственности: все цифры, которые я собираюсь привести в качестве примеров, не являются точными, но показывают принцип того, что происходит за кулисами.

Давайте рассмотрим два случая:

(1) int1 = 1000, int2= 3, double = 3.0Первый метод даст вам: (1000.0 / 3) * 3 == 333.33333 * 3.0 == 999.999...
Пока второе даст (1000 * 3.0) / 3 == 3000 / 3 == 1000
В этом сценарии - второй способ является более точным.

(2) int1 = 2, int2 = 2, double = Double.MAX_VALUE
Первый даст (2.0 / 2) * Double.MAX_VALUE == 1 * Double.MAX_VALUE == Double.MAX_VALUE
Пока второе даст (2 * Double.MAX_VALUE) / 2 - что приведет (в Java) к InfinityЯ не уверен, что двойной стандарт говорит об этих случаях, если он может переполниться или это всегда бесконечность - но это определенно проблема.
Итак, в данном случае - первый способ более точен.

Все может пойти сложнее, если integerс longс или double является float, поскольку есть длинные значения, которые не могут быть представлены doubles, поэтому потеря точности может произойти для больших double значения в этом случае, а в любом случае - большие double значения менее точны.

Вывод: что лучше, зависит от предметной области. В некоторых случаях первый метод должен быть лучше, а в некоторых первый. Это действительно зависит от значений int1,int2, а также double,
Тем не менее, AFAIK, общее правило с операциями двойной точности заключается в том, чтобы вычисления были как можно меньше (не создавайте огромные числа, а затем уменьшайте их обратно, делайте их как можно дольше). Эта проблема известна как потеря значащих цифр.

Ни один из них не является особенно быстрым, поскольку компилятор или процесс JIT в любом случае могут переупорядочить операцию для повышения эффективности.

Может быть, я неправильно понимаю ваше требование, но зачем вообще делать деление / умножение внутри цикла? Может быть, это даст те же результаты:

decimal r1 = 100.0m, r2 = 1000.0m, r = r2 - r1;
int n = 30;
decimal[] position = new double[n];

decimal diff = r / n;
decimal current = r1;

for (int i = 0; i < n; i++)
{
    position[i] = current;
    current += diff;
}
Другие вопросы по тегам