Optimize i64/f64 comparison Also fixes a subtle bug with excess precision that could surface under Valgrind.
Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/615d033e Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/615d033e Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/615d033e Branch: refs/heads/master Commit: 615d033e45b6fc768e62370780e322aaa68339ac Parents: 2d4038d Author: Nick Wellnhofer <[email protected]> Authored: Sun May 15 15:02:12 2016 +0200 Committer: Nick Wellnhofer <[email protected]> Committed: Sun May 15 17:29:38 2016 +0200 ---------------------------------------------------------------------- runtime/core/Clownfish/Num.c | 68 ++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 37 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/615d033e/runtime/core/Clownfish/Num.c ---------------------------------------------------------------------- diff --git a/runtime/core/Clownfish/Num.c b/runtime/core/Clownfish/Num.c index 3274b4a..3fc2357 100644 --- a/runtime/core/Clownfish/Num.c +++ b/runtime/core/Clownfish/Num.c @@ -44,12 +44,6 @@ #define POW_2_63 9223372036854775808.0 static int32_t -S_compare_f64(double a, double b); - -static int32_t -S_compare_i64(int64_t a, int64_t b); - -static int32_t S_compare_i64_f64(int64_t i64, double f64); static bool @@ -86,7 +80,9 @@ int32_t Float_Compare_To_IMP(Float *self, Obj *other) { if (Obj_is_a(other, FLOAT)) { Float *twin = (Float*)other; - return S_compare_f64(self->value, twin->value); + double a = self->value; + double b = twin->value; + return a < b ? -1 : a > b ? 1 : 0; } else if (Obj_is_a(other, INTEGER)) { Integer *twin = (Integer*)other; @@ -154,7 +150,9 @@ int32_t Int_Compare_To_IMP(Integer *self, Obj *other) { if (Obj_is_a(other, INTEGER)) { Integer *twin = (Integer*)other; - return S_compare_i64(self->value, twin->value); + int64_t a = self->value; + int64_t b = twin->value; + return a < b ? -1 : a > b ? 1 : 0; } else if (Obj_is_a(other, FLOAT)) { Float *twin = (Float*)other; @@ -187,26 +185,17 @@ Int_Clone_IMP(Integer *self) { } static int32_t -S_compare_f64(double a, double b) { - return a == b ? 0 : a < b ? -1 : 1; -} - -static int32_t -S_compare_i64(int64_t a, int64_t b) { - return a == b ? 0 : a < b ? -1 : 1; -} - -static int32_t S_compare_i64_f64(int64_t i64, double f64) { - int f64_comparison = S_compare_f64((double)i64, f64); - - // If the integer can be represented precisely as a double or the numbers - // compare as unequal when converted to double, the result is correct. - if ((i64 >= MIN_PRECISE_I64 && i64 <= MAX_PRECISE_I64) - || f64_comparison != 0 - ) { - return f64_comparison; - } + double i64_as_f64 = (double)i64; + // This conversion might not be precise. If the numbers compare as + // unequal, the result is still correct. + if (i64_as_f64 < f64) { return -1; } + if (i64_as_f64 > f64) { return 1; } + + // If the integer can be represented precisely as a double, the + // numbers are equal. Testing for (i64 < MAX_PRECISE_I64) is more + // efficient than (i64 <= MAX_PRECISE_I64) on 32-bit systems. + if (i64 >= MIN_PRECISE_I64 && i64 < MAX_PRECISE_I64) { return 0; } // Otherwise, the double is an integer. @@ -214,20 +203,23 @@ S_compare_i64_f64(int64_t i64, double f64) { // out of range. if (f64 == POW_2_63) { return -1; } - return S_compare_i64(i64, (int64_t)f64); + // llrint() can be faster than casting to int64_t but isn't as + // portable. + int64_t f64_as_i64 = (int64_t)f64; + return i64 < f64_as_i64 ? -1 : i64 > f64_as_i64 ? 1 : 0; } static bool S_equals_i64_f64(int64_t i64, double f64) { - bool equal = ((double)i64 == f64); - - // If the integer can be represented precisely as a double or the numbers - // compare as unequal when converted to double, the result is correct. - if ((i64 >= MIN_PRECISE_I64 && i64 <= MAX_PRECISE_I64) - || !equal - ) { - return equal; - } + double i64_as_f64 = (double)i64; + // This conversion might not be precise. If the numbers compare as + // unequal, the result is still correct. + if (i64_as_f64 != f64) { return false; } + + // If the integer can be represented precisely as a double, the + // numbers are equal. Testing for (i64 < MAX_PRECISE_I64) is more + // efficient than (i64 <= MAX_PRECISE_I64) on 32-bit systems. + if (i64 >= MIN_PRECISE_I64 && i64 < MAX_PRECISE_I64) { return true; } // Otherwise, the double is an integer. @@ -235,6 +227,8 @@ S_equals_i64_f64(int64_t i64, double f64) { // out of range. if (f64 == POW_2_63) { return false; } + // llrint() can be faster than casting to int64_t but isn't as + // portable. return i64 == (int64_t)f64; }
