The only thing that I want to add to the already excellent answers is to highlight a few contracts.
First contract, is that equality, inequality and comparison operations in modern Smalltalk are always based on comparing the exact value. At least, this is true on Dolphin, gnu, Pharo, Squeak.
It's not always been so. Take this C code for example:
int64_t i=1<<60+1;
double d=(double) i;
printf("%d\n',d==i);
Those two numbers do not have equal values (they can't because the integer requires 61 bits, while double only provide a 53 bits significand). Though the result of equality is true, because the integer value is converted to double BEFORE the test.
This was the case of most Smalltalk dialects too, in early 2000, 1/10 = 0.1
did answer true, despite the two numbers are not carrying the exact same value... Fortunately, we adopted wiser strategy of Scheme language since: compare exactly.
Now that we have a contract on equality, we can express further contracts on the conversions. First:
aFloat asTrueFraction = aFloat.
"which means that they share the exact same value"
"replace with asExactFraction in gst"
The second contract is this:
aFloat asMinimalDecimalFraction asFloat = aFloat.
"Though the decimal fraction may differ, it will always convert back to same float"
asMinimalDecimalFraction
will answer the shortest decimal fraction that will round back to the same Float. It's very related to printing a float shortly and accurately, and in fact share the same algorithm. This is exactly the same as repr
in Python. See also absPrintExactlyOn:
in Squeak/Pharo. Note that this is NOT a good name, because it does not print the EXACT value, but the SHORTEST value that will round back to the same float (hence, it can be used fearlessly in read/eval/print activities).
In Squeak, the way to print the exact decimal value of a Float is this:
aFloat printShowingMaxDecimalPlaces: Float emin - Float precision + 1.
This is because the minimal power of two that can be represented in double precision is
(2 raisedTo: Float emin - Float precision + 1) = Float fminDenormalized.
And because 1/2^n requires n places after decimal point to be printed (it is 5^n/10^n).
Though continued fractions are a nice thing, I am not aware of any contract concerning asApproximateFraction
. It may or may not round back to the same Float. The question is where we stop the recursion?
Historical notes: the conversion Integer>>asFloat
and Fraction>>asFloat
will answer the Float nearest to their exact value in modern Smalltalk, at least in gst, Squeak/Pharo. It was not the case in early 2000, and maybe still not the case in each and every dialect dialect. Written as a contract:
(aFraction - aFraction asFloat asTrueFraction) abs <= (aFraction - aFraction asFloat predecessor asTrueFraction) abs and: [
(aFraction - aFraction asFloat asTrueFraction) abs <= (aFraction - aFraction asFloat successor asTrueFraction) abs]
Failing to provide such basic properties ruin the chance to express clean and clear contracts of higher level. It also can be very misleading when you try to check and understand what happens.
Every Smalltalk implementation should care of these features (contracts) nowadays.