Как я могу получить лучший точный результат? - PullRequest
0 голосов
/ 15 мая 2009

Дано:

unsigned int a, b, c, d;

Я хочу:

d = a * b / c;

и (a * b) могут переполниться; также (b / c) может равняться нулю и давать меньшую точность.

Может быть, приведение к 64-битному формату заставит все работать, но я хочу знать, как лучше получить наиболее точный результат в d.

Есть ли хорошее решение?

Ответы [ 7 ]

5 голосов
/ 15 мая 2009

Я бы либо:

  • Приведение к 64 битам, если это будет работать для ваших диапазонов a, b и c.
  • Используйте библиотеку с бесконечной точностью, такую ​​как GMP
  • Приведите к float или double и обратно, если вы найдете эти результаты приемлемыми.
3 голосов
/ 15 мая 2009

Для лучшей точности / точности вы захотите сделать свои умножения перед делением. Как вы подразумеваете, вы захотите использовать что-то с вдвое большим количеством битов, чем int:

int64_t d = (int64_t) a * (int64_t) b;
d /= c;

Вам не нужны оба приведения, но они, вероятно, делают это немного яснее.

Обратите внимание, что если c достаточно мало, то d все равно может быть больше, чем int. Это может или не может быть проблемой для вас. Если вы уверены, что это не так, вы можете привести к int в конце.

1 голос
/ 15 мая 2009

Для вашей проблемы, как указано, я бы сделал d = (long long)a * b / c;

Нет смысла переходить на float, когда вам нужно только больше битов. Не нужно повторно декларировать или бросать все. Кастинга a достаточно, чтобы повысить b и c до большего размера в выражении.

1 голос
/ 15 мая 2009

Вы всегда можете сделать явную проверку на переполнение на a * b:

long long e = (long long) a * (long long) b;
if (e <= INT_MAX) {
    d = e / c;
} else {
    d = a * (b / c);
}

Конечно, это работает только для неотрицательных a, b, c. Если они могут быть отрицательными, вы также должны проверить INT_MIN.

[Обновить] Вы также можете проверить, какие из a и b больше и, следовательно, теряют меньшую точность при делении на c:

if (a >= b) {
    d = a / c * b;
} else {
    d = a * (b / c);
}
1 голос
/ 15 мая 2009

Используйте число с плавающей запятой или двойное, в арифметике с плавающей запятой, допускается деление на ноль , результаты будут положительной или отрицательной бесконечностью

0 голосов
/ 02 июля 2009

Я бы сделал что-то вроде следующего:

if(c){
    d = (long long)a * b;
    d /= c;
}
else{
    // some error code because div by 0 is not allowed
}
0 голосов
/ 15 мая 2009

Почему бы не использовать float или double? float (на чипах Intel) - это 32-разрядное число с плавающей запятой, поэтому вам не нужно 64-разрядное значение для операции?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...