Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion corpus
19 changes: 17 additions & 2 deletions src/ta_misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@
namespace pineforge {
namespace ta {

namespace {

constexpr double kPineFloatEqualityBand = 1e-10;

bool percentrank_less_equal(double lhs, double rhs) {
if (is_na(lhs) || is_na(rhs)) return false;
const bool equal =
lhs == rhs ||
(std::isfinite(lhs) && std::isfinite(rhs) &&
std::fabs(lhs - rhs) <= kPineFloatEqualityBand);
return lhs < rhs || equal;
}

} // namespace


// --- Linreg (Linear Regression) ---

Expand Down Expand Up @@ -87,7 +102,7 @@ double PercentRank::compute(double src) {
double v = buffer_[i];
if (is_na(v)) continue;
valid++;
if (v <= current) {
if (percentrank_less_equal(v, current)) {
count++;
}
}
Expand Down Expand Up @@ -240,7 +255,7 @@ double PercentRank::recompute(double src) {
double v = buffer_[i];
if (is_na(v)) continue;
valid++;
if (v <= current) count++;
if (percentrank_less_equal(v, current)) count++;
}
if (valid == 0) return na<double>();
return ((double)count / (double)valid) * 100.0;
Expand Down
76 changes: 76 additions & 0 deletions tests/test_ta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,82 @@ static int test_percentrank_pine_semantics() {
fails++;
}
}
{
// PercentRank uses Pine's fixed finite comparison band internally:
// values at most 1e-10 above the current sample compare equal.
ta::PercentRank causal(2);
causal.compute(-10.0);
causal.compute(-5.349999999999909);
double got = causal.compute(-5.350000000000136);
if (!near(got, 100.0)) {
std::printf("FAIL percentrank fixed-band causal: got %g want 100\n", got);
fails++;
}

ta::PercentRank inside(2);
inside.compute(-1.0);
inside.compute(9e-11);
got = inside.compute(0.0);
if (!near(got, 100.0)) {
std::printf("FAIL percentrank fixed-band inside: got %g want 100\n", got);
fails++;
}

ta::PercentRank endpoint(2);
endpoint.compute(-1.0);
endpoint.compute(1e-10);
got = endpoint.compute(0.0);
if (!near(got, 100.0)) {
std::printf("FAIL percentrank fixed-band endpoint: got %g want 100\n", got);
fails++;
}

ta::PercentRank outside(2);
outside.compute(-1.0);
outside.compute(1.1e-10);
got = outside.compute(0.0);
if (!near(got, 50.0)) {
std::printf("FAIL percentrank fixed-band outside: got %g want 50\n", got);
fails++;
}
}
{
const double inf = std::numeric_limits<double>::infinity();

ta::PercentRank positive_inf(2);
positive_inf.compute(-inf);
positive_inf.compute(inf);
double got = positive_inf.compute(inf);
if (!near(got, 100.0)) {
std::printf("FAIL percentrank positive infinity: got %g want 100\n", got);
fails++;
}

ta::PercentRank negative_inf(2);
negative_inf.compute(-inf);
negative_inf.compute(inf);
got = negative_inf.compute(-inf);
if (!near(got, 50.0)) {
std::printf("FAIL percentrank negative infinity: got %g want 50\n", got);
fails++;
}
}
{
ta::PercentRank recomputed(2);
recomputed.compute(-1.0);
recomputed.compute(1.9e-10);
recomputed.compute(0.0);
double got = recomputed.recompute(1e-10);

ta::PercentRank fresh(2);
fresh.compute(-1.0);
fresh.compute(1.9e-10);
double want = fresh.compute(1e-10);
if (!near(got, 100.0) || !near(got, want)) {
std::printf("FAIL percentrank fixed-band recompute: got %g want %g\n", got, want);
fails++;
}
}
return fails;
}

Expand Down
Loading