WxWidgets: простая математическая формула дает неверные результаты? - PullRequest
3 голосов
/ 09 ноября 2010

Мне очень нравятся WxWidgets, и я начал с программирования на C ++.Моя программа-пример конвертирует Цельсий в Фаренгейты из текстовой формы.Вот мой базовый код:

//get "100" from textbox
wxString szCelsius = TextCtrl1->GetValue();
long lCelsius;

//attempt to cast into long
szCelsius.ToLong(&lCelsius, 10);

//formula that works in normal cases to get fahrenheit
long lFahrenheit = ((9.f/5.f) * lCelsius + 32);

//SOMEHOW this works:
//long lFahrenheit = ((9.f/5.f) * 100 + 32);

//display debug info, note it displays lCelsius as 100
wxString debuginfo;
debuginfo << _T("deg C: ")  << lCelsius << _T("\n");
//displays incorrectly as 211
debuginfo << _T("deg F: ") << lFahrenheit << _T("\n");
//this displays 100
std::cout << lCelsius;
//this fails though
assert(lCelsius == 100);

Теперь с отладочной информацией lCelcius равен 100, как и ожидалось, но возвращает фаренгейт как 211 ​​вместо 212!Странно то, что формула отлично работает в чистом C, и когда я заменяю lCelsius на 100, она прекрасно работает, даже если в моей отладочной информации четко указано, что это 100.

Видите ли вы какие-либо очевидныепроблема или я просто не в состоянии сделать такую ​​простую вещь?Я не совсем уверен, что делает Wx, чтобы сделать его на один меньше, чем следовало бы.

EDIT : в том числе assert.h и запуск lCelsius == 100 не удается в отладчике, но std ::cout lCelsius возвращает 100. Должно быть что-то не так с Wx, которое искажает результат, но все еще имеет значение «100» ..

Ответы [ 2 ]

3 голосов
/ 09 ноября 2010

Значение 1.8 (равное 9/5) не может быть точно представлено в виде двоичного числа с плавающей запятой - в двоичном виде это повторяющаяся последовательность цифр (1.1100110011001100110011001100 ...) - аналогично тому, как повторяется 1/3 в десятичном формате.

Ближайшее представление в виде значения с плавающей запятой одинарной точности составляет чуть меньше 1,8 - это примерно 1,7999999523). Когда это число умножается на 100, это приводит к значению чуть меньше 180; и когда добавляется 32, получается число чуть меньше 212.

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

Причина, по которой этого не происходит, если вы используете литерал 100 в исходном коде вместо значения, предоставленного во время выполнения, заключается в том, что компилятор упростил выражение (9.f/5.f) * 100 во время компиляции до простого 180.

Если ваш компилятор поддерживает функцию C99 roundf() (объявлена ​​в math.h), вы можете использовать ее для округления до ближайшего целого числа:

long lFahrenheit = roundf((9.f/5.f) * lCelsius + 32);
1 голос
/ 09 ноября 2010

Вы можете попробовать отладить это на уровне сборки, чтобы увидеть более подробно, что происходит.

Кроме того, в качестве стилистического предложения ведущий термин может быть записан как (9.f / 5.f), который избавляет от приведений и его легче читать.

Я также спрашиваю, почему вы используете long для температуры, мне кажется, что большинство температур (особенно с учетом Фаренгейта) находятся в диапазоне, поддерживаемом простым int.

...