fractions: String Constructor Rejects a Negative Denominator (tested)
The two-argument numeric form of Fraction happily accepts a negative denominator and normalizes the sign onto the numerator. We measured what happens when the same ratio is passed as a string instead -- the string parser is stricter about where the minus sign is allowed to go, and rejects forms the numeric constructor accepts without complaint.
1. The numeric two-argument form accepts a negative denominator and normalizes it
from fractions import Fraction
print(Fraction(3, -4))
print(Fraction(-3, 4))
print(Fraction(-3, -4))
-3/4
-3/4
3/4
Measured: Fraction(3, -4) and Fraction(-3, 4) both normalize to the same value, -3/4; Fraction(-3, -4) normalizes to 3/4. The numeric constructor doesn't care which argument carries the minus sign.
2. The string form only accepts the minus sign on the numerator
from fractions import Fraction
print(Fraction("-3/4"))
try:
print(Fraction("3/-4"))
except ValueError as e:
print("ValueError:", e)
-3/4
ValueError: Invalid literal for Fraction: '3/-4'
Measured: Fraction("-3/4") parses fine and matches the numeric form above. But Fraction("3/-4") -- the mathematically identical ratio, with the sign moved to the denominator -- raises ValueError. The string grammar simply doesn't allow a sign after the /.
3. A negative sign on both sides of the string also raises, even though it's valid for numbers
from fractions import Fraction
print(Fraction(-3, -4))
try:
print(Fraction("-3/-4"))
except ValueError as e:
print("ValueError:", e)
3/4
ValueError: Invalid literal for Fraction: '-3/-4'
Measured: Fraction(-3, -4) (numeric) normalizes to 3/4, but the equivalent string "-3/-4" raises ValueError rather than normalizing the same way. The string parser's regex only permits an optional leading minus before the whole literal, not a second one after the slash.
This matters for any code that builds a fraction string by interpolating a numerator and a possibly-negative denominator (e.g. f"{num}/{den}" from measured or user-supplied values) and later re-parses it with Fraction(s) -- the round trip silently breaks the moment den is negative, even though constructing the same value directly with Fraction(num, den) would have worked. If a denominator might be negative, normalize the sign onto the numerator before formatting the string (or just pass both parts to the two-argument numeric constructor instead of building a string).
Frequently asked questions
Does Fraction("3/-4") work the same as Fraction(3, -4)?
No -- we measured Fraction(3, -4) normalizing fine to -3/4, but the string form Fraction("3/-4") raising ValueError: Invalid literal for Fraction. The string parser only accepts a minus sign on the numerator side, not after the slash.
Why does Fraction("-3/-4") raise ValueError when Fraction(-3, -4) works?
The two-argument numeric constructor normalizes signs from either or both arguments, returning 3/4 for Fraction(-3, -4). The string parser's grammar only allows one optional leading minus sign before the whole literal, so a second minus after the slash is rejected even though the value it represents is valid.
How do I safely build a Fraction string from a numerator and denominator that might be negative?
Normalize the sign onto the numerator before formatting (so the denominator is always positive), or skip the string entirely and pass both values to the numeric two-argument constructor, Fraction(num, den), which accepts a negative value in either position.
What error does Fraction() raise for an invalid string?
ValueError, with a message like "Invalid literal for Fraction: '3/-4'" -- we confirmed this is raised specifically when a minus sign appears after the slash, even though the equivalent negative value is accepted fine by the numeric two-argument constructor.
Written with AI assistance. Measured on 2026-10-04 with Python 3.13.5's built-in fractions module: constructed Fractions from the numeric two-argument form with a negative denominator, then from equivalent string literals with the minus sign on the numerator, the denominator, and both. PlainHeaders is our extension; Python's fractions module is built in.