Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesFor a regular Python float, use math.modf(x). It returns (fractional_part, integer_part), both as floats, and both carry the sign of x. So math.modf(3.14)[0] is about 0.14, and math.modf(-3.14)[0] is about -0.14. The details that trip people up are negative numbers, % 1, and float rounding noise.
The quick answer
import math
fraction, whole = math.modf(3.14)
print(fraction) # 0.14000000000000012
print(whole) # 3.0
If you only need the fraction, write math.modf(x)[0]. Python’s math documentation describes modf as returning “the fractional and integer parts of x. Both results carry the sign of x and are floats.”
As an Amazon Associate I earn from qualifying purchases.
The same signed result comes from truncation: x - math.trunc(x). math.trunc rounds toward zero, not toward negative infinity.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Negative numbers: pick a convention first
“Fractional part” means two different things for negative inputs. Decide which one you need.
#1 Best Overall
- Signed (truncation):
math.modf(-3.14)[0]or-3.14 - math.trunc(-3.14)gives about-0.14. Use this when the fraction should keep the number’s sign, so that whole + fraction rebuilds the original. - Floor-based, in [0, 1):
x - math.floor(x)gives about0.86for-3.14. Use this for things like wrapping a phase or position into a unit interval.
math.floor rounds toward negative infinity, and that is the whole difference.
Why % 1 is not the same as modf
For built-in floats, % follows the floor-division remainder convention. With a positive divisor, the result is nonnegative. So -3.14 % 1 is about 0.86, matching the floor-based formula, not modf. For positive numbers all of these agree, which is why the difference only shows up once negatives arrive.
Rank #2
Comparing the approaches
| Approach | Result for -3.14 | Best fit | Caution |
|---|---|---|---|
math.modf(x)[0] |
about -0.14 (signed) | Clean split into fraction and whole | Returns a tuple; float approximation applies |
x - math.trunc(x) |
about -0.14 (signed) | Explicit truncation-based fraction | Think about input type and special values |
x - math.floor(x) |
about 0.86 (nonnegative) | Fraction in [0, 1) | Different meaning for negatives |
x % 1 |
about 0.86 (float remainder rule) | When remainder semantics are wanted | Not interchangeable with modf for negatives |
Decimal arithmetic |
-0.14 exactly, with % 1 or x - x // 1 |
Base-10 exact input | Don’t mix with float; context and special values matter |
Why you see 0.14000000000000012
Python floats are binary. Many decimal fractions, including 3.14, have no exact finite binary form, so the stored value is a close approximation. Subtracting the whole part exposes that tiny error. math.modf is working correctly. The Python tutorial recommends the decimal module when you need exact decimal representation.
If you only need to display the result, round it for output: round(fraction, 2) or f"{fraction:.2f}".
Exact decimal fractions with Decimal
Build the Decimal from a string, not a float, or the float’s error comes along with it.
from decimal import Decimal
x = Decimal("3.14")
fraction = x - x // 1
print(fraction) # 0.14
Decimal // truncates toward zero, and Decimal % returns a result with the sign of the dividend. So Decimal("-3.14") % 1 is -0.14, which is the opposite of the float behavior above. Decimal also supports NaN and infinities, and operations on them may signal exceptions, so the finite examples here are not universal.
Getting the digits after the point
If you want the digits themselves (for example 14), the fraction as a float is the wrong tool because of the binary noise. Work from a decimal string instead:
from decimal import Decimal
d = Decimal("3.14")
digits = str(d).split(".")[1] # "14"
This assumes the value has a fractional part in plain notation. Handle integers (no “.”), negative signs and exponent notation if your input can contain them.
Quick Recap
Best Value
Edge cases
- Very large floats have no fractional bits, because the spacing between representable values is wider than 1. Their fractional part is
0.0. For example,math.modf(1e16)returns(0.0, 1e16). - Result order is fraction first, integer second, the reverse of what many people expect.
- Both outputs of
modfare floats, so the whole part is3.0, not3. Wrap it inint()if you need an integer.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




