2013-05-17 5 views
0

У меня есть класс чтения csv-файла, содержащий числовые записи с не более чем двумя цифрами после десятичной точки.Странное поведение при преобразовании float в int

int ReadCellWithFloat(int cellNumber, int multiplier) throw (FSByFieldException) 
{ 
    GoToCell(cellNumber); 
    float number; 
    FileStream >> number; 
std::cout << "what we've got: " << number; 
    if (!FileStream.good()) 
    { 
     throw BuildException(FSByFieldException::NOT_FLOAT); 
    } 
    while (multiplier--) 
    { 
     number *= 10; 
    } 
std::cout << ' ' << number << ' ' << (int) number << std::endl; 
    PassCell(); // here meaning pass comma separator 
    return (int) number; 
} 

Для ячейки, содержащей поплавок «8,49» выходы выход:

what we've got: 8.49 849 848 

Почему 849 превращается в 848 на литье в целое и как это исправить?

+6

См. [Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой] (http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) – syam

+1

Этот вопрос задан сотни раз в SO – GuLearn

+1

@ RaymondChen Я пришел сюда из очереди просмотра, но действительно ли это дубликат, учитывая, что другой вопрос касается C#? – sashoalm

ответ

2

849 превращается в 848, потому что чистый литье просто усекает значение. Итак, (int) 4.8 == 4. Вы можете подумать, что 8.49 * 10 * 10 - это 849, но это не обязательно так. Это примерно так же, как и 849, поскольку архитектура может представлять собой нечто вроде 848.999999 ... которое становится 848 после усечения.

Чтобы исправить это, вы можете использовать (int) round(number), и вы также можете быть заинтересованы в взглянуть на функции ceil() и floor() и общая информация по вопросам точности с плавающей запятой, чтобы избежать проблем с числом сравнений и этажерки.

0

Число 8.49 не может быть точно представлено, поэтому, когда вы умножаете его на 10 два раза вы в конечном итоге с чем-то более похожим 848.99999, когда я пытаюсь это:

number = 8.49 ; 
number *= 10 ; 
number *= 10 ; 

std::cout << std::fixed << number << std::endl ; 

я 848.999939, когда вы cast до integer он усекается, и поэтому вы получаете 848. Использование roundf даст вам результат вы хотите:

std::cout << std::fixed << roundf(number) << std::endl ; 

в C++ 11 вы бы просто использовать round.

+0

Число 849 точно представимо. Проблема заключается в 8.49 не 849. –

+0

@ RaymondChen Это было действительно неловко сформулировано, я исправил, чтобы было ясно, хотя пример действительно разъясняет, что я имел в виду. –

0

Это связано с тем, что поплавки хранятся в двоичном пространстве. Это link покажет вам, что значение 8.49 лучше всего представить в виде поплавка 8.4899997.

Поэтому, когда вы бросаете float как int, он будет решать фактическое значение float, а не округленное значение.

Есть несколько способов, вы можете обойти эту проблему: 1) Используйте двойную точность (который стихнет эту проблему, а не не устранить ее) 2) округлить число 848.99, используя математические библиотеки. 3) Добавьте 0.5 ко всем номерам перед литьем как int.

+0

Я не знаком с какой-либо реализацией кастинга, которая округляет поплавок; насколько я знаю. –

+0

Вы правы. Я согласен, что он всегда усекает. Извините, если какая-то часть моего ответа запуталась. Моим решением этой проблемы было бы добавить 0.5 к числу перед литьем как int, например (int) (849.99997 + 0.5). Таким образом, вы можете избежать включения математических библиотек для этой простой задачи. –

Смежные вопросы