Sometimes the question of "what is more intuitive" is not easily answered; so I'm asking the overall community for an opinion: how to handle the "equivalence" operator for the decimal library I'm writing.
First a bit of background: in a typical decimal library, one not only stores the decimal digits, but also the "significance" of a number.
For example, the population of the US is currently 328.2 million. Or, written mathematically: 328.2 x 10^6 or if written in the IEEE spec format: 3.282E8. But you would NOT write it as 328200000. That is because 328200000 is an exact number. Whereas 3.282E8 is not.
Every decimal number has a "precision of significance" (the number of significant digits) and a "precision of scale" (think number of decimals before/after the decimal point). In Python, the inverted scale is called prec.
This isn't a trivia thing. It affects everything including math operations. For example
3.282E8 + 1 adds up to 3.282E8 (the same number)
328200000 + 1 adds up to 328200001
THE QUESTIONS
How do I represent EXACT comparisons? This is where the digits, significance, and scale must all match. And,
How do I represent comparative magnitude? This is where the numbers are the same within the context of significance?
One possibility is to use == for magnitude and === for exactness. For example:
# assume that any number ending in "m" is a decimal type.
assert 2E2m == 2E2m # aka 2 x 10 ^ 2 which is 200 but with less significance
assert 2E2m == 200m
assert 2E2m == 272m
assert 2E2m != 300m
assert 123.45m == 123m
assert 2E2m === 2E2m
assert 2E2m !== 200m
assert 2E2m !== 272m
assert 2E2m !== 300m
assert 123.45m !== 123m
I can also see switching those: making == mean exact and === mean equivalent.
Or perhaps using other symbols. I'm open to suggestions.
Looking at decimal libraries in other languages; the answer to this question is not always consistent.
^= is bad because it overlaps with in-place pow.
== should be exact binary repr equal. Does your library allow multiple representations for the same value? In that case == should canonicalize first.
For approximate equal I suggest not using an operator because it would be easier to search for and you can configure the tolerance.
Furthermore in some application relative difference is what you want and in others absolute difference.
@mratsim
When you ask "Does your library allow multiple representations for the same value?" I assume you mean same numeric value and significance combination. They are unique when properly encoded, so normalization is not needed.*
I like the idea of avoiding a symbol for a simple numeric equivalence. Perhaps:
assert 2.0m != 2.00m
assert equivalent(2.0m, 2.00m)
(* get's me to thinking: perhaps the binary and hex import functions should check for improper encoding and fix that. If one is getting the 128-bit binary image of the number from, say, a database or BSON file, maybe I should not assume it was done right. )
And also, having looked at the math library equivalent:
assert almostEqual(2.0m, 2.01m)
assert almostEqual(2.01m, 2.0m)
assert not almostEqual(2.0m, 2.06m) # 2.06 rounds up to 2.1 which is not equal to 2.0
assert not almostEqual(2.0m, 2.10m)