Это совершенно другая идея ...
Используйте аппаратное обеспечение с плавающей запятой, но дополните его своими собственными целочисленными показателями.Другими словами, BigFloat.significand
будет числом с плавающей точкой вместо целого числа.
Тогда вы можете использовать ldexp
и frexp
, чтобы фактический показатель степени с плавающей точкой был равен нулю.Это должны быть одиночные машинные инструкции.
Таким образом, умножение BigFloat становится:
r.significand = left.significand * right.significand
r.exponent = left.exponent + right.exponent
tmp =
(фактическийпоказатель r.significand из frexp) r.exponent += tmp
- (используйте ldexp для вычитания
tmp
из фактического показателя r.significand
)
К сожалению,последние два шага требуют frexp
и ldexp
, которые, как показывают результаты поиска, недоступны в C #.Таким образом, вам, возможно, придется записать этот бит на C.
...
Или, на самом деле ...
Использовать числа с плавающей запятой для значимостей, но просто сохранитьих нормализовали между 1 и 2. Итак, снова используйте поплавки для значений и умножьте их следующим образом:
r.significand = left.significand * right.significand;
r.exponent = left.exponent + right.exponent;
if (r.significand >= 2) {
r.significand /= 2;
r.exponent += 1;
}
assert (r.significand >= 1 && r.significand < 2); // for debugging...
Это должно работать до тех пор, пока вы сохраняете инвариант, упомянутый в assert ().(Потому что если x между 1 и 2, а y между 1 и 2, то x * y находится между 1 и 4, поэтому на этапе нормализации просто нужно проверить, когда значимое и произведение находится между 2 и 4.)
Вам также нужно будет нормализовать результаты дополнений и т. Д., Но я подозреваю, что вы уже это делаете.
Хотя вам все-таки понадобится специальный случай нуля: -).
[править, чтобы конкретизировать frexp
версию]
BigFloat BigFloat::normalize(BigFloat b)
{
double temp = b.significand;
double tempexp = b.exponent;
double temp2, tempexp2;
temp2 = frexp(temp, &tempexp2);
// Need to test temp2 for infinity and NaN here
tempexp += tempexp2;
if (tempexp < MIN_EXP)
// underflow!
if (tempexp > MAX_EXP)
// overflow!
BigFloat r;
r.exponent = tempexp;
r.significand = temp2;
}
В другихсловами, я бы предложил вынести это как «нормализующую» подпрограмму, поскольку, по-видимому, вы хотите использовать ее после сложений, вычитаний, умножений и делений.
И затем есть все ключевые случаи, о которых нужно беспокоиться...
Вы, вероятно, хотите обработать недостаточное значение, возвращая ноль.Переполнение зависит от ваших вкусов;должно быть либо ошибкой, либо + -infinity.Наконец, если результатом frexp () является бесконечность или NaN, значение tempexp2
не определено, поэтому вы можете также проверить эти случаи.