From 019718fbb3fd04111d9dbd5c6b356504f3b3485d Mon Sep 17 00:00:00 2001 From: Cory Buecker Date: Sat, 2 Mar 2024 20:22:10 -0600 Subject: [PATCH] fix bug with percentile function Sorting arrays of small doubles was inconsistent with the previous method --- Gemfile.lock | 2 +- changelog.md | 4 ++++ ext/ruby_native_statistics/conversions.c | 15 +++++++++++++-- lib/ruby_native_statistics/version.rb | 2 +- test/dispersion_test.rb | 15 +++++++++++++++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2fe9dc7..40777c5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - ruby_native_statistics (1.0.2) + ruby_native_statistics (1.1.0) rake-compiler (~> 1.2) GEM diff --git a/changelog.md b/changelog.md index 110d417..3cd0ee1 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,7 @@ +# Version 1.1.0 + +- Fix percentile bug reported by @Gbird22 + # Version 1.0.3 - Update all supported Ruby versions diff --git a/ext/ruby_native_statistics/conversions.c b/ext/ruby_native_statistics/conversions.c index edf8c3e..f5928b3 100644 --- a/ext/ruby_native_statistics/conversions.c +++ b/ext/ruby_native_statistics/conversions.c @@ -1,4 +1,5 @@ #include "conversions.h" +#include "float.h" int compare_doubles(const void *a, const void *b) { @@ -8,7 +9,17 @@ int compare_doubles(const void *a, const void *b) double cmp_a = *dbl_a; double cmp_b = *dbl_b; - return (cmp_a - cmp_b); + if (fabs(cmp_a - cmp_b) <= (DBL_EPSILON * fabs(cmp_a + cmp_b))) + { + return 0; + } + + if (cmp_a > cmp_b) + { + return 1; + } + + return -1; } double *sorted_ruby_array(VALUE array, long array_length) @@ -20,7 +31,7 @@ double *sorted_ruby_array(VALUE array, long array_length) if (working_array == NULL) { - rb_raise(rb_eStandardError, "unknown problem calculating median (possibly array is too large)"); + rb_raise(rb_eStandardError, "unknown problem sorting array (possibly array is too large)"); } for (i = 0; i < array_length; i++) diff --git a/lib/ruby_native_statistics/version.rb b/lib/ruby_native_statistics/version.rb index a4529dc..75f4d62 100644 --- a/lib/ruby_native_statistics/version.rb +++ b/lib/ruby_native_statistics/version.rb @@ -1,3 +1,3 @@ module RubyNativeStatistics - VERSION = "1.0.3" + VERSION = "1.1.0" end diff --git a/test/dispersion_test.rb b/test/dispersion_test.rb index e0123ef..fa27ad4 100644 --- a/test/dispersion_test.rb +++ b/test/dispersion_test.rb @@ -129,4 +129,19 @@ def test_percentile_floats assert_in_delta 0.73895928, array.percentile(0.333), 0.000001 assert_in_delta 11.43290852, array.percentile(0.928), 0.000001 end + + def test_percentile_repeating + array = [5.4, 5.3, 5.2, 5.4, 5.2].to_a.shuffle + + assert_in_delta 5.4, array.percentile(0.9), 0.000001 + assert_in_delta 5.2, array.percentile(0.1), 0.000001 + assert_in_delta 5.3, array.percentile(0.5), 0.000001 + assert_in_delta 5.26, array.percentile(0.4), 0.000001 + end + + def test_percentile_duplicates + array = [5.2, 5.2, 5.2, 5.2, 5.2].to_a.shuffle + + assert_in_delta 5.2, array.percentile(0.9), 0.000001 + end end