Как я могу доверять приведению от двойного к целому?

Я работал над простым кодом для создания гистограмм и нашел следующий код:

double value = 1.2;
double bucketSize = 0.4;
double bucketId = value / bucketSize;

std::cout << "bucketId as double: " << bucketId << std::endl;
std::cout << "bucketId as int: " << int(bucketId) << std::endl;

приводит к сумасшедшему выводу:

bucketId as double: 3
bucketId as int: 2

что в основном разрушает мое доверие к компьютерам;) при поиске подходящего bucketId для value при создании гистограммы.

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

(На всякий случай) Пожалуйста, не предлагайте добавлять 0.5 результат разделения до приведения к int поскольку, по-видимому, в некоторых случаях это не очень хорошо работает (например, double value = 3; double bucketSize = 2;)

Заранее спасибо.

6

Решение

В комментариях вы говорите, что хотите Integer part of the result, Ну, к сожалению, double Результат 1.2 / 0.4 просто бывает 2.9999999999999996 (На моей машине. Вы можете увидеть точное значение с cout используя std::setprecision) и, следовательно, целая часть результата 2, Это, как вы знаете, объясняется тем, что не все числа могут быть представлены числами с плавающей запятой, и потому что операции с плавающей запятой подвержены ошибкам.

Взятие целой части числа с плавающей запятой находится на том же уровне, что и сравнение чисел с плавающей запятой с равенством; Вы не получите последовательных результатов. если ты должен есть точные результаты, генеральный Решение состоит в том, чтобы вообще не использовать числа с плавающей запятой, а вместо этого фиксированную.

Как и при сравнении равенства, вы можете обойти проблему с соответствующим эпсилон значение. В этом случае вы можете добавить (или вычесть, если отрицательный) очень маленькое число с плавающей запятой к результату, прежде чем принимать целочисленную часть. Добавленное число должно быть больше наибольшей возможной ошибки, которую может иметь число, но меньше наименьшего числа точности, которое вы должны поддерживать (чтобы значение 9,999 не становилось 10, если вы должны поддерживать точность до 0,001). Выяснить хороший номер для этого может быть довольно сложно.

3

Другие решения

Я основываю это более или менее на некоторых ваших комментариях к другим.
Чтобы получить целую часть, решение состоит в том, чтобы использовать modf, Но
целая часть 1.2 / 0.4 вполне может быть 2, и не 3;
0.4 не представлен в машинной плавающей запятой (большинство
их, по крайней мере), так что вы делите на что-то очень близко к
0.4,

Настоящий вопрос в том, что вы на самом деле хотите. Если вы ищете
по своему усмотрению (существует ли такое слово) в зависимости от
bucketSizeтогда правильный способ сделать это — использовать
масштабированные целые числа вокруг:

int value = 12;
int bucketSize = 4;
int bucketId = value / bucketSize;

а потом:

std::cout << "bucketId as double: " << bucketId / 10.0 << std::endl;
std::cout << "bucketId as int: " << bucketId / 10 << std::endl;

В противном случае, если вы хотите сохранить значения как двойные, вы будете
должны решить, насколько близко близко для преобразования в int,
затем используйте свою собственную функцию:

int
asInt( double d )
{
double results;
double frac = modf( d, &results );
if ( frac > 1.0 - yourEpsilonHere ) {
results += 1.0;
}
return results;
}

Вам решать, какое значение подходит для
yourEpsilonHere; это зависит от приложения. (Один раз
Я использовал эту технику, мы использовали 1E-9, Это не значит что
это подходит для вас, однако.)

4

использование std::lround, Возвращает ближайшее целое число к вашему двойному числу.

#include<numeric>
double value = 1.2;
double bucketSize = 0.4;
double bucketId = value / bucketSize;

std::cout << "bucketId as int: " << std::lround(bucketId) << std::endl;

Обратите внимание, что 3.0/2.0 все еще может привести к неожиданным результатам, в зависимости от того, является ли результат 1.4999998 или же 1.5000001наивно говорится.

3

Может быть исправлено десятичное приближение?

(int)(value * 100)/(int)(bucketSize *100)
3

Если вы хотите 0,25 до него, чтобы вернуть следующий номер (как (double)1.75 в (int)2) использовать int(floor(buckedId+0.25)),

Дело в том, сколько вы хотите, чтобы округлить до предыдущего числа.

2
#include<iostream>
#include <limits>
int main()
{
double value = 1.200000000000000;
double bucketSize = 0.4000000000000000;
double bucketId = value / bucketSize;
std::cout.precision(16);
std::cout << "bucketId as double: " <<  std::fixed << bucketId << std::endl;
std::cout << "bucketId as int: " << int(bucketId) << std::endl;
return 1;
}

попробуйте это в вашей системе, вы бы что-то вроде

BucketId как двойной: 2.9999999999999996

bucketId как int: 2

и чем с

std::cout.precision(15);

ты бы

двойной идентификатор bucketId: 3.000000000000000

bucketId как int: 2

это происходит из-за того, что предел точности double равен 15, вы также можете попытаться сконструировать с long double и варьировать точность.

2

Я бы предложил что-то вроде

double d = 1.4 / 0.4;
int whole = (int)d;
int nextWhole = whole + 1;

int result = whole;

if (fabs(d - nextWhole) < EPSILON) result = nextWhole;

(это работает для положительных чисел)

По сути, если ваш номер настолько близок к следующему целому числу, что это не имеет значения, этот код будет использовать следующее целое число.

2

Ты можешь использовать round()

http://www.cplusplus.com/reference/cmath/round/

Я считаю, что это всегда отнимает 0,5 цифры от нуля, так что если это плохо для вашего случая, это может быть не оптимальным решением

1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector