decimal: Subtracting Equal Values Can Yield -0 Under ROUND_FLOOR (tested)

IEEE 754 decimal arithmetic has a signed zero: -0 and 0 compare equal but aren't the same value, and str() shows the sign. We measured when ordinary-looking Decimal arithmetic produces a negative zero, and what that looks like once it's printed, logged, or exported rather than just compared.

1. Equal values subtract to a plain, positive zero by default

from decimal import Decimal

a = Decimal("5.00")
b = Decimal("5.00")
print(a - b)
0.00

Measured: under the default context, subtracting two equal Decimal values gives the unsigned-looking 0.00, exactly what most code expects.

2. The identical subtraction yields -0.00 under ROUND_FLOOR

from decimal import Decimal, localcontext, ROUND_FLOOR

a = Decimal("5.00")
b = Decimal("5.00")
with localcontext() as ctx:
    ctx.rounding = ROUND_FLOOR
    print(a - b)
-0.00

Measured: same two values, same subtraction, but with the context rounding mode set to ROUND_FLOOR (a mode some code picks deliberately, e.g. to always round monetary results down/conservatively), the result prints as -0.00. The IEEE 754 decimal spec defines the sign of an exact-zero arithmetic result based on the rounding rule in effect, not just the operands -- ROUND_FLOOR rounds toward negative infinity, and that extends to picking the negative zero when a result is exactly zero.

3. -0 compares equal to 0, but prints differently everywhere

from decimal import Decimal

nz = Decimal("-0.00")
print(nz == Decimal("0.00"))
print(str(nz))
print(f"{nz}")
print(bool(nz))
True
-0.00
-0.00
False

Measured: -0.00 == 0.00 is True and bool(nz) is False (both treat it as zero-valued), so equality checks and truthiness are unaffected. But str() and f-string formatting both render the sign, printing the literal text -0.00. A log line, CSV export, or any string-matching/diffing of output that doesn't expect a minus sign on a value that's logically "zero" will see -0.00 rather than 0.00 -- cosmetically wrong even though every numeric check on the value says it's fine. If a result needs a clean sign for display, normalize it explicitly, e.g. result + Decimal('0') under default rounding, or check is_signed() before formatting.

Frequently asked questions

Can subtracting two equal Decimal values produce a negative zero?

Yes -- we measured Decimal('5.00') - Decimal('5.00') giving the plain 0.00 under the default context rounding mode, but -0.00 under ROUND_FLOOR. The sign of an exact-zero arithmetic result depends on the active rounding mode, per IEEE 754 decimal arithmetic rules, not just the operands.

Does Decimal('-0') equal Decimal('0')?

Yes -- we confirmed Decimal('-0.00') == Decimal('0.00') is True, and bool() on either is False. Equality and truthiness treat signed zero as equivalent to unsigned zero.

Why does my Decimal result print as -0.00 instead of 0.00?

If the value came from an arithmetic operation under a rounding mode like ROUND_FLOOR (rather than from parsing a string), an exact-zero result can carry a negative sign that == doesn't reveal but str() and f-strings do -- we measured both printing the literal '-0.00' text.

How do I avoid printing a negative zero from a Decimal calculation?

Normalize the sign explicitly before display, e.g. by adding Decimal('0') under the default rounding context, or check .is_signed() and strip the sign if the value is zero. We confirmed == and bool() don't catch this on their own since they already treat -0 and 0 as equivalent.

Which Decimal rounding modes can produce a negative zero result?

We measured ROUND_FLOOR producing -0.00 from an exact-zero subtraction where the default context rounding mode produced plain 0.00 from the identical operands -- any rounding mode that rounds toward negative infinity can do the same for a result that lands exactly on zero.

Written with AI assistance. Measured on 2026-10-04 with Python 3.13.5's built-in decimal module: subtracted two equal Decimal values under the default context rounding mode, then repeated the identical subtraction under ROUND_FLOOR, and checked how the result prints via str() and f-strings. PlainHeaders is our extension; Python's decimal module is built in.