Batu Lab NotesPractical developer guides

Use Decimal compare_total for a deterministic sort of special values

By Batu · English technical notes

Python can use Decimal.compare_total() for a deterministic sort of special values by adapting it with functools.cmp_to_key() and passing the result as sorted’s key. This is a representation order, not an arithmetic order: it can distinguish -0 from 0 and signaling NaN from quiet NaN.

The synthetic input deliberately contains Decimal('-0'), Decimal('0'), Decimal('NaN'), and Decimal('sNaN'). Ordinary equality cannot supply a total sorting rule here: the example records that a quiet NaN does not equal itself. The previous candidate’s list equality assertion was also inappropriate because comparing lists reaches the NaN element, so it evaluates false despite unchanged values. The repaired assertion instead saves and compares each item’s as_tuple() representation, which checks that the original list stayed intact without invoking NaN equality.

The correction calls left.compare_total(right) for each comparison and converts its Decimal('-1'), Decimal('0'), or Decimal('1') result to an integer. cmp_to_key() turns that two-argument comparator into the key interface required by sorted. For this exact input, the documented representation order is -0, 0, sNaN, then NaN. The assertions establish only this literal fixture and do not make NaNs numerically equal or validate a domain-specific business order.

functools.cmp_to_key() was added in Python 3.2. The Decimal.compare_total documentation defines its total ordering of abstract representations, and cmp_to_key documentation explains comparator adaptation.

AI assistance disclosure: This article was prepared with AI assistance and checked with the shown synthetic example.

from decimal import Decimal
from functools import cmp_to_key

values = [Decimal("-0"), Decimal("0"), Decimal("NaN"), Decimal("sNaN")]
input_representations = [value.as_tuple() for value in values]

nan_equals_itself = values[2] == values[2]
ordered = sorted(
    values,
    key=cmp_to_key(lambda left, right: int(left.compare_total(right))),
)
labels = [str(value) for value in ordered]

assert [value.as_tuple() for value in values] == input_representations
assert nan_equals_itself is False
assert labels == ["-0", "0", "sNaN", "NaN"]

print("input:", [str(value) for value in values])
print("NaN equals itself:", nan_equals_itself)
print("compare_total order:", labels)
input: ['-0', '0', 'NaN', 'sNaN']
NaN equals itself: False
compare_total order: ['-0', '0', 'sNaN', 'NaN']