2014-12-10 2 views
1

Я хотел бы преобразовать float или double в целое число с фиксированной запятой в C. Я ищу наиболее подходящее (надежное) решение этой проблемы, учитывая спецификацию языка C.Поплавок (или двойной) в десятичное целое число с фиксированной точкой

Проблема, например, вдоль линий этого:

double d = 15.6; 
int i; 
... 
i = (int)(d * 10.0); /* I am excepting getting 156 in 'i' */ 

Соответствующие элементы стандарта (с использованием C99 один, ISO/IEC 9899: ТС3 см this question для загрузки), насколько я вижу, :

  • 6.3.1.4/1: Когда конечное значение действительного типа с плавающим преобразуются в тип целого числа, отличный _Bool, дробная часть отбрасывается (то есть, значение усекается к нулю). (...)
  • 6.4.4.2/3: (...) Для десятичных плавающих констант (...) результат является либо ближайшим представимым значением, либо большим или меньшим представимым значением, немедленно примыкающим к ближайшему представляемое значение, выбранное в соответствии с реализацией. (...)

15.6 в примере не получил точное представление в IEEE двойной, так что я бы за исключением d, чтобы получить что-то чуть выше или чуть ниже. Проблема заключается в части «чуть ниже», тогда i в этом примере не получит результат, который был бы исключен (вместо этого получился бы 155).

Мой очевидно взять бы что-то вроде этого (с учетом нулевой или только положительные значения):

i = (int)(d * 10.0 + 0.5); 

По крайней мере, если я интерпретировал стандарт C правильно. Я спрашиваю, потому что из-за «поведения, определенного реализацией», можно получить согласованный результат, в то время как в реальности какая-то другая реализация может нарушить работу программы, поэтому проб и ошибок не является адекватным методом поиска подходящего решения.

В частности the following question связывает эту проблему, которая, как я считаю, имеет неверный принятый ответ.

+0

В стандарте C не предусматривается использование плавающей точки IEEE. Теоретически возможно, что 'd' представляется как некоторое безумное число, например' 15.65'. Это становится гораздо более вероятным, так как 'd' становится больше. Я бы сказал, что 'd' уже не так, и вы должны использовать библиотеку произвольной точности с самого начала. – Kevin

+0

@Kevin: Я только что упомянул IEEE, поскольку даже в IEEE этот пример показывает проблему. Если вы бросите «тяжелую артиллерию», строго придерживающуюся формулировки стандарта C, конечно, у вас скоро будет не только одна большая зияющая дыра. Библиотека произвольной точности может быть не всегда осуществимой, например, включить внедренную цель. – Jubatian

+0

Ну, тогда вам нужно решить, что вы цените больше: производительность или правильность? – Kevin

ответ

2

C99 определяет круглую функцию для этой цели. Используйте это, а затем переведите в int.

UPDATE: для C89, вы можете попробовать

double y = floor(x); 
double z = x == y ? x : floor(2.0*x-y); 

Это должно дать такой же, как C99 round, за исключением того, что отрицательные числа с дробными частями, равными 0,5 округляется вверх (как Java), и нули могут (это основано на аналогичном трюке due to Arch Robinson).

+0

Да, нашел (я имею в виду в стандарте)! Я строгий парень из C89, работающий во встроенной памяти, даже не заметил, что C99 может иметь это. Я принимаю это, так как я упоминал о стандарте C99 в своем посте, поэтому я действительно не могу сказать ничего плохого в этом решении. Было бы неплохо, если бы ответ был в конечном итоге отредактирован, чтобы включить также совместимое с C89 решение. – Jubatian

+0

См. Мой обновленный ответ, возможно, это поможет. –

+0

Интересный (статья тоже), и кажется функциональным, ОК от C89. Кстати, 'z = floor (x + 0.5)', почему этого было бы недостаточно? C89 (и что-то выше) определяет 'floor', чтобы вернуть наибольшее целое число, не превышающее его операнд, поэтому оно должно работать нормально и для негативов. Арк Робинсон использовал «trunc», который для негативов раундов к нулю (аналогично моему примеру, если я иду по стандарту 6.3.1.4/1) - это еще одна история о том, что существование 'fesetround' в C99 делает эту точку стандартный пахнущий фиктивный). – Jubatian

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