3 ms·
The issues come in the mixed type cases. Consider this C program: #include <stdio.h> int main(void) { long x = 9007199254740992; // 2^53
by StefanKarpinski 13y ago
The issues come in the mixed type cases. Consider this C program:
#include <stdio.h>
int main(void) {
long x = 9007199254740992; // 2^53
double y = 9007199254740992.0; // 2.0^53
long z = 9007199254740993; // 2^53+1
printf("x == y: %d\n", x == y);
printf("y == z: %d\n", y == z);
printf("x == z: %d\n", x == z);
return 0;
}
Its output shows that "==" is not transitive in C:
x == y: 1
y == z: 1
x == z: 0
On the other hand, "==" is transitive in Julia:
julia> x, y, z = 9007199254740992, 9007199254740992.0, 9007199254740993;
julia> x == y, y == z, x == z
(true,false,false)
You have to be careful: if you compare "y" and "z" by converting "z" to Float64, they appear to be equal even though they represent entirely different integer values. When subtracting them, however, one does convert them to Float64, so their difference is zero:
julia> z - y
0.0
This kind of issue is why it's so important to have a theory of what "==" means.
- stephencanon 13y agoAh, of course. So what you’ve essentially done is to make it so that comparisons and arithmetic use distinct promotion rules; comparisons are done without conversions that change value, whereas conversions are applied for arithmetic? Transitivity failures that arise from clearly specified conversion rules are at least easy to understand once you encounter them. If I understand correctly, to avoid losing transitivity, you’ve introduced a divergence between how types behave in arithmetic and comparisons, and broken the equivalence between x - y == 0 and x == y. To my mind as a numericist and low-level programmer, that’s much worse; it’s the path that Excel started to follow a long time ago, and it leads to all sorts of maddening details when corner cases collide. My preferred solution for high-level languages is “promote the operands to a type that can represent both exactly” (for the case of double and int64, this would be a floating-point type with at least 64 bits of significand and 11 exponent bits, like the Intel 80-bit format or quad). For a compute-oriented language, there are good performance reasons to avoid that, but it’s definitely the solution that complies with the principle of least surprise.