С кодом ниже я получаю результат «4.31 43099».
double f = atof("4.31");
long ff = f * 10000L;
std::cout << f << ' ' << ff << '\n';
Если я поменяю «double f» на «float f». Я получаю ожидаемый результат «4,31 43100». Я не уверен, что замена «double» на «float» является хорошим решением. Есть ли хорошее решение, чтобы убедиться, что я получу «43100»?
Проблема в том, что с плавающей точкой неточна природа, когда речь идет о десятичных числах. Десятичное число может быть округлено вверх или вниз при преобразовании в двоичное число, в зависимости от того, какое значение является ближайшим.
В этом случае вы просто хотите убедиться, что если число было округлено в меньшую сторону, оно округлено в большую сторону. Вы делаете это, добавляя наименьшую возможную сумму к значению, что делается с помощью nextafter
функция если у вас есть C ++ 11:
long ff = std::nextafter(f, 1.1*f) * 10000L;
Если у вас нет nextafter
Вы можете приблизить его с numeric_limits
.
long ff = (f * (1.0 + std::numeric_limits<double>::epsilon())) * 10000L;
Я только что увидел ваш комментарий, что вы используете только 4 десятичных знака, так что это будет проще, но менее надежно:
long ff = (f * 1.0000001) * 10000L;
Вы не сможете устранить ошибки в арифметике с плавающей запятой (хотя при правильном анализе вы можете вычислить ошибку). Для случайного использования, что вы можете сделать, чтобы получить более интуитивные результаты, это заменить встроенное преобразование с плавающей точкой в целочисленное (которое выполняет усечение) обычным округлением:
double f = atof("4.31");
long ff = std::round(f * 10000L);
std::cout << f << ' ' << ff << '\n';
Это должно вывести то, что вы ожидаете: 4.31 43100
Также нет смысла использовать 10000L
потому что, независимо от того, какой тип интегрального типа вы используете, он все равно преобразуется в f
Тип с плавающей точкой для умножения. просто используйте std::round(f * 10000.0);
Со стандартными типами C — я сомневаюсь.
Есть много значений, которые не могут быть представлены в этих битах — они фактически требуют больше места для хранения. Так что процессор с плавающей точкой просто использует максимально возможный.
Плавающие числа с указанием не могут хранить все значения, которые, как вы думаете, могли бы — количество битов ограничено — вы не можете поместить более 4 миллиардов различных значений в 32 бита. И это только первое ограничение.
Значения с плавающей точкой (в C) представлены как: sign - one sign bit, power - bits which defines the power of two for the number, significand - the bits that actually make the number
,
Ваш фактический номер sign * significand * 2 inpowerof(power - normalization).
Double — 1 бит знака, 15 битов мощности (нормализовано, чтобы быть положительным, но это не точка) и 48 битов, чтобы представить значение;
Это много, но недостаточно для представления всех значений, особенно когда они не могут быть легко представлены в виде конечной суммы степеней двух: как двоичный код 1010.101101 (101). Например, он не может точно представлять такие значения, как 1/3 = 0,333333 (3). Это второе ограничение.
Попробуйте прочитать — приличное понимание преимуществ и недостатков арифметики с плавающей запятой может быть очень полезным:
http://en.wikipedia.org/wiki/Floating_point а также http://homepage.cs.uiowa.edu/~atkinson/m170.dir/overton.pdf
Здесь были некоторые запутанные ответы! Происходит следующее: 4.31 нельзя точно представить как число с одинарной или двойной точностью. Оказывается, что ближайшее представимое число с одинарной точностью чуть больше 4,31, а ближайшее представимое число с двойной точностью чуть меньше 4,31. Когда значение с плавающей запятой присваивается целочисленной переменной, оно округляется до нуля (а не до ближайшего целого числа!).
Так что если f
с одинарной точностью, f * 10000L
больше 43100, поэтому округляется до 43100. А если f
двойная точность, f * 10000L
меньше 43100, поэтому округляется до 43099.
Комментарий н.м. предполагает f * 10000L + 0.5
что я считаю лучшим решением.