Разделите unsigned long на size_t и присвойте результат двойному

Мне нужно разделить unsigned long int на size_t (возвращенный из измерения массива с помощью size() ) следующим образом:

vector<string> mapped_samples;
vector<double> mean;
vector<unsigned long> feature_sum;
/* elaboration here */
mean.at(index) = feature_sum.at(index) /mapped_samples.size();

но таким образом происходит целочисленное деление (я теряю десятичную часть. Это нехорошо)

Поэтому я могу сделать:

 mean.at(index) = feature_sum.at(index) / double(mapped_samples.size());

Но таким образом feature_sum.at(index) автоматически преобразуется (Временная копия) в double, и я могу потерять точность. Как я могу решить вопрос? Я должен использовать какую-то библиотеку?

Это может быть потеря точности при преобразовании unsigned long в double (поскольку значение unsigned long может быть больше, чем максимальное значение double). Значение unsigned long представляет собой сумму функций (положительных значений). Выборки признаков могут быть 1000000 или более, а сумма значений признаков может быть огромной. Максимальное значение функции равно 2000, таким образом: 2000*1000000 или более

(я использую С++11)


person Umbert    schedule 24.05.2017    source источник
comment
Вы можете использовать Boost.Multiprecision библиотека; он способен к целочисленным вычислениям произвольной точности и арифметике с плавающей запятой.   -  person Jason R    schedule 24.05.2017
comment
@JasonR В C++ нет более простого решения? Сейчас я сосредоточен на другом, и я бы не стал изучать библиотеку для одной операции, если только это не единственный способ   -  person Umbert    schedule 24.05.2017
comment
Вы не указали, какая точность вам нужна   -  person keith    schedule 24.05.2017
comment
Еще одна вещь, о которой следует помнить: вы сказали, что size_t представляет размерность массива, но вас беспокоит потеря точности при преобразовании его в double. double может содержать 53-битные целые числа без потери точности. Вам действительно нужно поддерживать значения больше 2^53 - 1?   -  person Jason R    schedule 24.05.2017
comment
@JasonR Это может быть потеря точности, когда вы конвертируете unsigned long в double (поскольку значение unsigned long может быть больше, чем максимальное значение double). Значение unsigned long представляет собой сумму функций (положительных значений). Выборки признаков могут быть 1000000 или более, а сумма значений признаков может быть огромной. Максимальное значение функции равно 2000, таким образом: 2000*1000000 или более   -  person Umbert    schedule 24.05.2017
comment
@Umbert: максимальное значение unsigned long составляет ~ 2 ^ 64, а максимальное двойное значение составляет ~ 2 ^ 300. Проблем с диапазонами нет.   -  person Mooing Duck    schedule 24.05.2017
comment
2000*1000000 — это всего лишь десятизначное число. 2^53 — шестнадцатизначное число. У вас есть много возможностей, прежде чем вам придется беспокоиться о потере точности.   -  person Solomon Slow    schedule 24.05.2017
comment
@MooingDuck Я знаю, что максимальная длина типов C++ не является стандартной. Что гарантирует, что всегда случается, что double больше, чем unsigned long?   -  person Umbert    schedule 24.05.2017
comment
Если вы не работаете на экзотической платформе, double всегда будет определяться стандартом IEEE-754, поэтому его диапазон указывается явно. Основываясь на этом и описании того, что представляет значение size_t, которое у вас есть, вам не нужно беспокоиться о потере точности при преобразовании в double. Могу поспорить, что пройдет много времени, прежде чем вы когда-либо получите массив с размером, сравнимым с 2^53 по размеру.   -  person Jason R    schedule 24.05.2017
comment
@jameslarge и вообще на любом компиляторе и любой системе максимальный двойник С++ имеет 16 цифр?   -  person Umbert    schedule 24.05.2017
comment
@JasonR и Джеймс правы. double почти везде 8 байт, и в моем случае это нормально. Спасибо   -  person Umbert    schedule 24.05.2017
comment
@Umbert, подавляющее большинство аппаратных и программных систем с плавающей запятой соответствуют стандарту IEEE 754. В C/C++ float обычно означает формат IEEE 754 binary32, а double обычно означает формат binary64, который дает вам 16-значную точность. Если ваша система поддерживает long double, возможно, это не формат, отличный от IEEE, но почти наверняка он будет иметь точность более 16 цифр.   -  person Solomon Slow    schedule 26.05.2017


Ответы (3)


Вы можете попробовать использовать std::div

Вдоль линий

auto dv = std::div(feature_sum.at(index), mapped_samples.size());

double mean = dv.quot + dv.rem / double(mapped_samples.size());
person Severin Pappadeux    schedule 24.05.2017

Вы можете использовать:

// Grab the integral part of the division
auto v1 = feature_sum.at(index)/mapped_samples.size();

// Grab the remainder of the division
auto v2 = feature_sum.at(index)%mapped_samples.size();

// Dividing 1.0*v2 is unlikely to lose precision
mean.at(index) = v1 + static_cast<double>(v2)/mapped_samples.size();
person R Sahu    schedule 24.05.2017
comment
то же, что и версия std::div - person Severin Pappadeux; 24.05.2017
comment
@SeverinPappadeux, правда. - person R Sahu; 24.05.2017
comment
Это более точно, чем оригинал? - person Mooing Duck; 24.05.2017
comment
Использование 1.0*v2/mapped_samples.size() показывает, что вы достаточно уверены в своих предпочтениях оператора и т. д. Более безопасным и читабельным является double(v2)/mapped_samples.size(). - person Walter; 24.05.2017
comment
@MooingDuck, да. Вы, скорее всего, потеряете точность при преобразовании feature_sum.at(index) в double, чем при преобразовании v2 в double. - person R Sahu; 24.05.2017
comment
@RSahu Я не согласен, см. мой ответ. - person Walter; 24.05.2017
comment
@ Уолтер, я бегло взглянул на твой ответ, но не совсем понял. Похоже, вы имели дело с этим аспектом чисел с плавающей запятой больше, чем я. - person R Sahu; 24.05.2017

вы не можете сделать лучше (если хотите сохранить результат как double), чем простой

std::uint64_t x=some_value, y=some_other_value;
auto mean = double(x)/double(y);

потому что относительная точность усеченной формы правильного результата с использованием float128

auto improved = double(float128(x)/float128(x))

обычно одинакова (для типичных входных данных — могут быть редкие входные данные, где возможно улучшение). Оба имеют относительную ошибку, определяемую длиной мантиссы для double (53 бита). Таким образом, простой ответ: либо используйте более точный тип, чем double для вашего среднего значения, либо забудьте об этой проблеме.


Чтобы увидеть относительную точность, предположим, что

x=a*(1+e);   // a=double(x)
y=b*(1+f);   // b=double(y)

где e, f имеют порядок 2^-53.

Тогда «правильное» частное — это первый порядок в e и f.

(x/y) = (a/b) * (1 + e - f)

Преобразование этого в double влечет за собой другую относительную ошибку порядка 2^-53, то есть того же порядка, что и ошибка (a/b), результат наивного

mean = double(x)/double(y).

Конечно, e и f могут договориться об отмене, когда можно добиться большей точности методами, предложенными в других ответах, но обычно точность нельзя улучшить.

person Walter    schedule 24.05.2017