fix ZAM "cat" of doubles/times to include trailing ".0" per normal BiF behavior

This commit is contained in:
Vern Paxson 2024-03-20 14:15:40 -07:00 committed by Tim Wojtulewicz
parent 86d1812d49
commit 4cafacf90b
5 changed files with 48 additions and 15 deletions

View file

@ -143,12 +143,7 @@ void ODesc::Add(double d, bool no_exp) {
Add(tmp);
auto approx_equal = [](double a, double b, double tolerance = 1e-6) -> bool {
auto v = a - b;
return v < 0 ? -v < tolerance : v < tolerance;
};
if ( approx_equal(d, nearbyint(d), 1e-9) && std::isfinite(d) && ! strchr(tmp, 'e') )
if ( util::approx_equal(d, nearbyint(d), 1e-9) && std::isfinite(d) && ! strchr(tmp, 'e') )
// disambiguate from integer
Add(".0");
}

View file

@ -534,11 +534,6 @@ void IntervalVal::ValDescribe(ODesc* d) const {
bool did_one = false;
constexpr auto last_idx = units.size() - 1;
auto approx_equal = [](double a, double b, double tolerance = 1e-6) -> bool {
auto v = a - b;
return v < 0 ? -v < tolerance : v < tolerance;
};
for ( size_t i = 0; i < units.size(); ++i ) {
auto unit = units[i].first;
auto word = units[i].second;
@ -547,7 +542,7 @@ void IntervalVal::ValDescribe(ODesc* d) const {
if ( i == last_idx ) {
to_print = v / unit;
if ( approx_equal(to_print, 0) ) {
if ( util::approx_equal(to_print, 0, 1e-6) ) {
if ( ! did_one )
d->Add("0 secs");
@ -571,7 +566,7 @@ void IntervalVal::ValDescribe(ODesc* d) const {
d->SP();
d->Add(word);
if ( ! approx_equal(to_print, 1) && ! approx_equal(to_print, -1) )
if ( ! util::approx_equal(to_print, 1, 1e-6) && ! util::approx_equal(to_print, -1, 1e-6) )
d->Add("s");
did_one = true;

View file

@ -68,10 +68,18 @@ void FixedCatArg::RenderInto(ZVal* zframe, int slot, char*& res) {
break;
case TYPE_DOUBLE:
case TYPE_TIME:
n = modp_dtoa2(z.AsDouble(), res, 6);
case TYPE_TIME: {
auto d = z.AsDouble();
n = modp_dtoa2(d, res, 6);
res += n;
if ( util::approx_equal(d, nearbyint(d), 1e-9) && std::isfinite(d) && ! strchr(tmp, 'e') ) {
// disambiguate from integer
*(res++) = '.';
*(res++) = '0';
}
break;
}
case TYPE_PATTERN:
text = z.AsPattern()->AsPattern()->PatternText();

View file

@ -2550,6 +2550,32 @@ TEST_CASE("util split") {
}
}
TEST_CASE("util approx_equal") {
CHECK(approx_equal(47.0, 47.0) == true);
CHECK(approx_equal(47.0, -47.0) == false);
CHECK(approx_equal(47.00001, 47.00002) == false);
CHECK(approx_equal(47.00001, 47.00002, 1e-5) == true);
CHECK(approx_equal(47.0, -47.0, 1e2) == true);
CHECK(approx_equal(47.0, -47.0, 94 + 1e-10) == true);
CHECK(approx_equal(47.0, -47.0, 94) == false);
constexpr auto inf = std::numeric_limits<double>::infinity();
CHECK_FALSE(approx_equal(inf, inf));
CHECK_FALSE(approx_equal(-inf, inf));
CHECK_FALSE(approx_equal(inf, -inf));
CHECK_FALSE(approx_equal(inf, inf, inf));
constexpr auto qnan = std::numeric_limits<double>::quiet_NaN(); // There's also `signaling_NaN`.
CHECK_FALSE(approx_equal(qnan, qnan));
CHECK_FALSE(approx_equal(-qnan, qnan));
CHECK_FALSE(approx_equal(qnan, -qnan));
}
/**
* Returns whether two double values are approximately equal within some tolerance value.
*/
bool approx_equal(double a, double b, double tolerance) { return std::abs(a - b) < std::abs(tolerance); }
} // namespace zeek::util
extern "C" void out_of_memory(const char* where) {

View file

@ -571,6 +571,15 @@ std::string json_escape_utf8(const std::string& val, bool escape_printable_contr
*/
std::string json_escape_utf8(const char* val, size_t val_size, bool escape_printable_controls = true);
/**
* Checks for values that are approximately equal.
* @param a first value to compare
* @param b second value to compare
* @param tolerance how close they need to be to deem them "approximately equal"
* @return true if `a` is within the given tolerance of `b`, false otherwise
*/
bool approx_equal(double a, double b, double tolerance = std::numeric_limits<double>::epsilon());
/**
* Splits a string at all occurrences of a delimiter. Successive occurrences
* of the delimiter will be split into multiple pieces.