#c #floating-point #c99 #floor #libm
#c #значение с плавающей запятой #c99 #этаж #libm
Вопрос:
У меня есть переменная x
типа float
, и мне нужна ее дробная часть. Я знаю, что могу получить это с помощью
x - floorf(x)
, илиfmodf(x, 1.0f)
Мои вопросы: всегда ли один из них предпочтительнее другого? Являются ли они фактически одинаковыми? Есть ли третья альтернатива, которую я мог бы рассмотреть?
Примечания:
- Если ответ зависит от используемого мной процессора, давайте сделаем его x86_64, и если вы можете подробнее рассказать о других процессорах, это было бы неплохо.
- Пожалуйста, убедитесь и обратитесь к поведению при отрицательных значениях
x
. Я не возражаю против того или иного поведения, но мне нужно знать, что это за поведение.
Комментарии:
1. По крайней мере, ответ не должен зависеть от процессора.
2. Я никогда не слышал о fmod(), поэтому мне пришлось поиграть с ним. Когда исходное число равно нулю или меньше, результаты будут другими. Имеет ли это значение?
Ответ №1:
Есть ли третья альтернатива, которую я мог бы рассмотреть?
Для этого есть специальная функция. modff существует для разложения числа на его целые и дробные части.
float modff( float arg, float* iptr );
Разлагает заданное значение с плавающей запятой
arg
на целые и дробные части, каждая из которых имеет тот же тип и знак,arg
что и . Целая часть (в формате с плавающей запятой) хранится в объекте, на который указываетiptr
.
Ответ №2:
Я бы сказал, что x - floorf(x)
это довольно хорошо (точно), за исключением угловых случаев
- он имеет неправильный знаковый бит для отрицательного нуля или любого другого отрицательного целого числа с плавающей запятой (мы могли бы ожидать, что дробная часть будет иметь тот же знаковый бит).
- это не так хорошо работает с inf
modff учитывает знаковый бит -0,0 как для частей int, так и для частей frac и отвечает /-0,0 для дробной части /-inf — по крайней мере, если реализация поддерживает стандарт IEC 60559 (IEEE 754).
Обоснование для inf может быть следующим: поскольку каждое число с плавающей запятой с точностью более 2 ^ имеет нулевую дробную часть, тогда это должно быть верно и для бесконечного числа с плавающей запятой.
Это незначительно, но, тем не менее, отличается.
РЕДАКТИРОВАТЬ Ошибка, конечно, как указано @StoryTeller-UnslanderMonica наиболее очевидным недостатком x - floor(x)
является случай отрицательной плавающей запятой с дробной частью, потому что, примененный к -2.25, он вернет, например, 0.75, что не то, что мы ожидаем…
Поскольку используется метка c99, x - truncf(x)
было бы более правильным, но все еще страдают от незначительных проблем, на которых я изначально сосредоточился.
Комментарии:
1.Для negative
x
floorf
будет ближайшим целым числом меньшеx
. Итак, скажем-3.2
, мы получим-4.0
, что, в свою очередь, приведет-3.2 - (-4.0) = 0.8
к — что является не просто неправильным знаком.2. @StoryTeller-UnslanderMonica Аргхх, спасибо, что указали на это, я был так сосредоточен на части со знаком нулевой дроби, что пропустил самую очевидную проблему!