Какой способ является более точным?
Мне нужно разделить числовой диапазон на несколько сегментов, имеющих одинаковую длину. Но я не могу решить, какой путь более точный. Например:
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
, поскольку есть длинные значения, которые не могут быть представлены double
s, поэтому потеря точности может произойти для больших 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;
}