2013-04-02 2 views
7

Округление результата деления на ближайшее целое число равно pretty simple. Но я пытаюсь округлить результат деления, так что последующая операция даст наилучшее приближение. Это лучше всего объяснить простой функции:Round y = x * x до ближайшего

const int halfbits = std::numeric_limits<unsigned int>::digits/2; 
unsigned x = foo(); // Likely big. 
unsigned x_div = x >> halfbits; // Rounded down 
unsigned y = x_div * x_div; // Will fit due to division. 

Я могу округлить x_div до ближайшего добавлением 1<<(halfbits-1). Но так как x² не является линейной функцией, y вообще не округляется правильно. Есть ли простой и более точный способ расчета (x*x) >> (halfbits*2) без использования больших типов?

Я думаю, что добавление 3<<(halfbits-3) в x_div улучшает округление, но не может доказать, что это лучшее решение. Кроме того, может ли это быть обобщено для xⁿ?

Редактировать: по многочисленным просьбам Я беру на себя смелость «переводить» вопрос в чистых арифметических терминах (ни одна из этих бит-сдвигающих вещей ...).
Примечание: все деления после этого являются целыми делениями, например 13/3 будут 4.
Проблема: мы не можем вычислить x^2, потому что x большой, поэтому вместо этого мы хотели бы вычислить (x^2)/(2^N).
Для этого вычислим
x_div = X/SQRT (2^N)
, который мы тогда площадь:
у = x_div * x_div

Однако этот результат, как правило, не хватает точного значения (x^2)/(2^N) и ОП предлагает добавить 0,5 * sqrt (2^N) или, может быть, 0,375 * sqrt (2^N), чтобы лучше приблизиться к результату ...

Как ответ Oli Charlesworth предлагает гораздо лучший способ добраться до фактическое значение, считая x^2 as (x_hi + x_lo)^2.

+7

Я не потерялся с бит-операциями, но разве вы могли бы перефразировать операции в математическом центре и менее ориентированном? Я не пытаюсь быть педантичным, я просто думаю, что это поможет прояснить проблему и помочь заявить о своей цели. –

+0

Несколько вещей, которые нужно попробовать: 'x_div_down * x_div_up',' x_div_near * x_div_near'. Если вы готовы потратить время, вы можете вычислить '(x_div * x_rest) >> (halfbit-1)' и исправить результат. –

+0

Итак, чтобы быть ясным, подразумеваемым вашим '(x * x) >> (halfbits * 2)', вы действительно хотите * un-rounded * бит-точный результат этого? Или вы хотите, чтобы математический результат округлился правильно? – hyde

ответ

8

Принимая во внимание, что усечение x_div приведет к ошибке ошибки не более 1, с x_div*x_div ошибка может быть до 1<<(half_digits+2).

Чтобы понять, почему, считают, что мы можем выразить эту квадратуру следующим образом (с использованием long multplication):

x * x = (x_lo + x_hi) * (x_lo + x_hi) 
     = x_lo^2 + x_hi^2 + 2*x_lo*x_hi 

где x_lo и x_hi являются нижними и верхними половинами x соответственно. С некоторым хорошим ASCII искусства, мы можем рассмотреть, как все они выстраиваются:

MSB  :  :  :  LSB 
    +------+------+  :  : 
    | x_hi^2 |  :  : 
    +-----++-----++-----+:  : 
    :  | 2*x_lo*x_hi |:  : 
    :  +------++-----++------+ 
    :  :  | x_lo^2 | 
    :  :  +------+------+ 
    :<----------->:  :  : 
    Our result 

Как видим, условия низкого порядка влияют на несколько битов в конечном итоге.

Однако каждое из этих терминов должно вписываться в исходный тип без переполнения. Поэтому при соответствующем переключении и маскировании мы можем получить желаемый результат.

Конечно, компилятор/аппаратное обеспечение делает это все для вас, если вы используете более крупный тип, поэтому вы должны просто сделать это, если у вас есть опция.

+0

Это то, к чему я стремился ... однако мне вдруг стало интересно: мы не знаем, вписывается ли 'x * x' в' unsigned', и мне непонятно, какое округление должно быть применено к результату ... –

+0

@ MatthieuM: Это не имеет значения, хотя. Нас интересует только '(x * x) >> (half_bits * 2)'. При соответствующем переключении мы можем использовать приведенные выше условия для получения правильного результата. –

+0

Использование бит 'n',' x_div' будет иметь бит 'n/2'. Максимальное значение, хранящееся в битах 'n', равно' 2^n - 1'. Поскольку '(2^n -1) - (2^(n/2) -1) * (2^(n/2) -1)' всегда больше нуля, мы знаем, что 'x_div * x_div' никогда не будет переполнение 'n' бит. –

-4

Используйте TYPE int для "y". Думаю, это решило бы цель.

+0

Примечание: нет ничего плохого в том, что вы удаляете свои неправильные ответы на SO, и быстро получать 4 downvotes - это, как правило, хороший признак того, что ваш ответ неверен, даже если ни один из downvoters не потрудился оставить объяснение. – hyde

+0

@hyde: Ну, одно объяснение состоит в том, что это определенно не «решает цель»;) –

+0

@OliCharlesworth да, я определенно не имел в виду, что downvotes были неправильными. Я просто в лагере, который предпочитает оставлять комментарий при downvoting, даже если это просто «ваш ответ просто ... неправильный». – hyde

Смежные вопросы