Я работаю над классом для представления чисел в научной нотации с планом преодоления точности с плавающей запятой в вычислениях чисел...
Обычно, если у нас есть два числа с плавающей запятой, как показано на рисунке:
double a = 23456234.892;
double b = 0.00000314;
double c = a + b;
Мы можем столкнуться с проблемами точности, а также с ошибками округления.
Чтобы попытаться бороться с этим, я решил разработать класс, который работает на принципах выполнения вычислений в экспоненциальном представлении.
Вот основная структура моего класса:
template<uint16_t BASE = 10>
class Fpn { // Floating Point Notation
public:
constexpr uint16_t Base{BASE};
private:
double coefficient_;
int64_t exponent_;
public:
Fpn() : coefficient_{0}, exponent_{1} {}
Fpn( double coeff, uint64_t exp ) : coefficient_{coeff}, exponent_{exp} {}
Fpn(const Fpn& rhs) {
coefficient_ = rhs.coefficient_;
exponent_ = rhs.exponent_;
}
Fpn& operator=(const Fpn& rhs) {
coefficient_ = rhs.coefficieint_;
exponent_ = rhs.coefficieint_;
return *this;
}
// Unary Operators:
Fpn operator+() const { return *this; }
// Negation:
Fpn operator-(bool invert = false) {
if (!invert) return { Fpn(-coefficient_, exponent_); }
return Fpn(coefficient_, -exponent_);
}
// arithmetic operators:
};
Сейчас я работаю над своими арифметическими операторами: operator+(const &obj)
, operator+=(const &obj)
, operator-(const &obj)
, operator*
, operator*=
, operator/
, operator/=
...
Теперь, прежде чем мы сможем Add
или Subtract
, они должны быть одного порядка возведения в степень...
Я думал о создании частной вспомогательной функции для выполнения этого преобразования, а затем вызывал ее в моих операторах +
, +=
, -
и -=
...
В рамках моей функции я думаю о том, как выяснить, у какого из них показатель больше, а затем я хочу преобразовать объект более низкого порядка в объект более высокого порядка...
private:
void convert(Fpn& rhs) {
if(exponent_ == rhs.exponent_) return; // Don't need to do anything
// This will tell me which one has the higher exponent
auto higher_order = (exponent_ > rhs.exponent_) ? exponent_ : rhs.exponent_;
// This is where I'm getting a bit stumped...
auto difference = higher_order - /*? smaller of the two* ... another ternary check?*/
// I'm checking negative against positive so that if abs() doesn't have to be called...
// or would that not matter and just use `abs()` regardless?
// Also, once I figure out which one needs to be converted
// Then I have to convert the correct side RHS vs LHS...
// ????
if (difference < 0) { // negative case
coefficient_ *= (abs(difference)*Base);
exponent_ /= (abs(difference)*Base);
}
if (difference > 0) { // positive case
coefficient_ *= difference * Base;
exponent_ /= difference * Base;
}
}
Если показатель LHS
больше, чем RHS
, то я хочу преобразовать RHS
в LHS
и сделать то же самое, если RHS
больше LHS
...
Пожалуйста, не предлагайте std::scientific
Я знаю, что это существует, и это не то, что мне нужно.
Должен ли я затем использовать другой оператор ternary
, чтобы вернуть меньший из двух? Или без необходимости выполнять кучу условных проверок... существуют ли жизнеспособные алгоритмы с stl
или каким-либо типом лямбда-выражения, которые помогли бы в этой части процесса?
Кроме того, я вычисляю разницу между ними, а затем проверяю, является ли эта разница отрицательной или положительной... если она отрицательная, то я использую abs()
из <cmath>
, в противном случае, если она положительная, я не звоню abs()
... Теперь это это всего лишь побочный вопрос, поскольку он связан с этой функцией и ее дизайном ... Было бы лучше не иметь условной проверки и просто вызывать abs()
независимо от того, или было бы лучше перейти между ними?
Есть еще одна функция, которую мне нужно написать, которая будет еще одной вспомогательной функцией, которую некоторые из этих операторов будут вызывать после выполнения своих арифметических вычислений, чтобы преобразовать окончательный результат в значение, где его коэффициент находится между [1,10]
, если Base
равно 10
, но эта функция еще не написано, так как мне нужно закончить с этой функцией convert()
.
Я знаю, что я хотел бы сделать, но я немного борюсь с дизайнерскими решениями, пытаясь использовать соответствующие методы и алгоритмы.
Я ценю любые отзывы, советы, предложения и т.
std::decimal
? Или launchpad.net/std-decimal. Или decNumber++. Или... - person Asteroids With Wings   schedule 19.08.2020int64_t
У меня тоже была опечатка для типаexponent
, он должен был бытьint64_t
а неuint64_t
... И база шаблонная... - person Francis Cugler   schedule 19.08.2020std::decimal::decimal128
. Например, на два порядка. Как вы думаете, зачем вам нужны показатели степени до ±9 223 372 036 854 775 807? Это кажется маловероятным. - person Asteroids With Wings   schedule 19.08.2020double coefficient_;
целым числом. Вашvoid convert(Fpn& rhs)
выполняет нормализацию числа с плавающей запятой, верно? - person KamilCuk   schedule 19.08.2020abs
или не использовать его, учтите, что либоabs
уже проверяет подписанность (и возвращает исходное значение, когда оно было положительным), и если это не так (потому что он просто устанавливает бит знака), то даже лучше, в любом случае вам не нужно проверять знак перед вызовомabs
- person 463035818_is_not_a_number   schedule 19.08.2020Plank
является наименьшей длиной! Вы всегда можете делить на 2 и никогда не получить 0, и вы можете делать это бесконечно и все равно никогда не достичь 0... Так что, кто знает, может быть, имея такую точность, можно изобрести целую новую область математики и физики! - person Francis Cugler   schedule 19.08.2020floating-point
, она также основана на различных системах счисления... Поэтому, если пользователь использует шаблон, скажем, ‹16›, тогда он будет выполнять логарифм 16 математики... если для него установлено значение ‹ 2> он будет выполнять двоичную базовую математику ... Ну, математика в настоящее время выполняется в десятичной системе счисления. Как только я заработаю основные функции, я буду беспокоиться о математических расчетах в других базах... - person Francis Cugler   schedule 19.08.2020