3

I have a variable x of type float, and I need its fractional part. I know I can get it with

  • x - floorf(x), or
  • fmodf(x, 1.0f)

My questions: Is one of these always preferable to the other? Are they effectively the same? Is there a third alternative I might consider?

Notes:

  • If the answer depends on the processor I'm using, let's make it x86_64, and if you can elaborate about other processors that would be nice.
  • Please make sure and refer to the behavior on negative values of x. I don't mind this behavior or that, but I need to know what the behavior is.
einpoklum
  • 118,144
  • 57
  • 340
  • 684

2 Answers2

6

Is there a third alternative I might consider?

There's the dedicated function for it. modff exists to decompose a number into its integral and fractional parts.

float modff( float arg, float* iptr );

Decomposes given floating point value arg into integral and fractional parts, each having the same type and sign as arg. The integral part (in floating-point format) is stored in the object pointed to by iptr.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
2

I'd say that x - floorf(x) is pretty good (exact), except in corner cases

  • it has the wrong sign bit for negative zero or any other negative whole float (we might expect the fraction part to wear the same sign bit).
  • it does not work that well with inf

modff does respect -0.0 sign bit for both int and frac part, and answer +/-0.0 for +/-inf fraction part - at least if implementation supports the IEC 60559 standard (IEEE 754).
A rationale for inf could be: since every float greater than 2^precision has a null fraction part, then it must be true for infinite float too.

That's minor, but nonetheless different.

EDIT Err, of course as pointed by @StoryTeller-UnslanderMonica the most obvious flaw of x - floor(x) is for the case of negative floating point with a fraction part, because applied to -2.25, it would return +0.75 for example, which is not what we expect...

Since c99 label is used, x - truncf(x) would be more correct, but still suffer from the minor problems onto which I initially focused.

aka.nice
  • 9,100
  • 1
  • 28
  • 40
  • 1
    For negative `x` `floorf` will be the closest integer less than `x`. So for say `-3.2` we will get `-4.0` which will in turn produce `-3.2 - (-4.0) = 0.8` - which is not just the wrong sign. – StoryTeller - Unslander Monica Jan 06 '21 at 13:30
  • @StoryTeller-UnslanderMonica Arghh, thanks for pointing this, I was so much focused on the sign of zero fraction part, that I missed the most obvious problem! – aka.nice Jan 06 '21 at 22:10