Getting the fractional part of a float without using modf()

64,738

Solution 1

If I understand your question correctly, you just want the part after the decimal right? You don't need it actually in a fraction (integer numerator and denominator)?

So we have some number, say 3.14159 and we want to end up with just 0.14159. Assuming our number is stored in float f;, we can do this:

f = f-(long)f;

Which, if we insert our number, works like this:

0.14159 = 3.14159 - 3;

What this does is remove the whole number portion of the float leaving only the decimal portion. When you convert the float to a long, it drops the decimal portion. Then when you subtract that from your original float, you're left with only the decimal portion. We need to use a long here because of the size of the float type (8 bytes on most systems). An integer (only 4 bytes on many systems) isn't necessarily large enough to cover the same range of numbers as a float, but a long should be.

Solution 2

As I suspected, modf does not use any arithmetic per se -- it's all shifts and masks, take a look here. Can't you use the same ideas on your platform?

Solution 3

I would recommend taking a look at how modf is implemented on the systems you use today. Check out uClibc's version.

http://git.uclibc.org/uClibc/tree/libm/s_modf.c

(For legal reasons, it appears to be BSD licensed, but you'd obviously want to double check)

Some of the macros are defined here.

Solution 4

There's a bug in your constants. You're basically trying to do a left shift of the number by 16 bits, mask off everything but the lower bits, then right shift by 16 bits again. Shifting is the same as multiplying by a power of 2, but you're not using a power of 2 - you're using 0xFFFF, which is off by 1. Replacing this with 0x10000 will make the formula work as intended.

Solution 5

I'm not completly sure, but I think that what you are doing is wrong, since you are only considering the mantissa and forgetting the exponent completely.

You need to use the exponent to shift the value in the mantissa to find the actual integer part.

For a description of the storage mechanism of 32bit floats, take a look here.

Share:
64,738
knight666
Author by

knight666

Game programming student at IGAD.

Updated on November 25, 2020

Comments

  • knight666
    knight666 over 3 years

    I'm developing for a platform without a math library, so I need to build my own tools. My current way of getting the fraction is to convert the float to fixed point (multiply with (float)0xFFFF, cast to int), get only the lower part (mask with 0xFFFF) and convert it back to a float again.

    However, the imprecision is killing me. I'm using my Frac() and InvFrac() functions to draw an anti-aliased line. Using modf I get a perfectly smooth line. With my own method pixels start jumping around due to precision loss.

    This is my code:

    const float fp_amount = (float)(0xFFFF);
    const float fp_amount_inv = 1.f / fp_amount;
    
    inline float Frac(float a_X)
    {
        return ((int)(a_X * fp_amount) & 0xFFFF) * fp_amount_inv;
    }
    
    inline float Frac(float a_X)
    {
        return (0xFFFF - (int)(a_X * fp_amount) & 0xFFFF) * fp_amount_inv;
    }
    

    Thanks in advance!