Fixing issue #1782

This patch establishes "nan", "inf" and "-inf" as
valid values for tl::Variant, so corresponding
PCell parameters can be serialized and are
properly managed.
This commit is contained in:
Matthias Koefferlein
2024-07-13 18:07:45 +02:00
parent f0b15e1049
commit 0df6339f4e
6 changed files with 353 additions and 5 deletions
@@ -361,6 +361,10 @@ module RBA
def param(name, type, description, args = {})
if ! args.is_a?(Hash)
raise("Too many positional arguments for 'param' (3 expected) - use named arguments for default value etc.")
end
if name !~ /^[_A-Za-z]\w*$/
raise "Invalid parameter name #{name} (needs to be a word)"
end
+58 -2
View File
@@ -226,9 +226,28 @@ bool skip_newline (const char *&cp)
}
}
// -------------------------------------------------------------------------
// Utility: case-insensitive compare of the first characters
static bool local_compare (const char *s1, const char *s2)
{
while (*s1 && *s2) {
uint32_t c1 = utf32_downcase (utf32_from_utf8 (s1));
uint32_t c2 = utf32_downcase (utf32_from_utf8 (s2));
if (c1 != c2) {
return false;
}
}
return true;
}
// -------------------------------------------------------------------------
// Utility: a strtod version that is independent of the locale
static std::string inf_string = "inf";
static std::string ninf_string = "-inf";
static std::string nan_string = "nan";
static std::string micron_format ("%.5f");
static std::string dbu_format ("%.2f");
@@ -244,12 +263,24 @@ void set_db_resolution (unsigned int ndigits)
std::string micron_to_string (double d)
{
return tl::sprintf (micron_format.c_str (), d);
if (std::isnan (d)) {
return nan_string;
} else if (std::isinf (d)) {
return d < 0 ? ninf_string : inf_string;
} else {
return tl::sprintf (micron_format.c_str (), d);
}
}
std::string db_to_string (double d)
{
return tl::sprintf (dbu_format.c_str (), d);
if (std::isnan (d)) {
return nan_string;
} else if (std::isinf (d)) {
return d < 0 ? ninf_string : inf_string;
} else {
return tl::sprintf (dbu_format.c_str (), d);
}
}
std::string to_upper_case (const std::string &s)
@@ -317,6 +348,18 @@ static double local_strtod (const char *cp, const char *&cp_new)
{
const char *cp0 = cp;
// special numerical values
if (local_compare (cp, nan_string.c_str ())) {
cp_new = cp + nan_string.size ();
return NAN;
} else if (local_compare (cp, inf_string.c_str ())) {
cp_new = cp + inf_string.size ();
return INFINITY;
} else if (local_compare (cp, ninf_string.c_str ())) {
cp_new = cp + ninf_string.size ();
return -INFINITY;
}
// Extract sign
double s = 1.0;
if (*cp == '-') {
@@ -376,6 +419,12 @@ static double local_strtod (const char *cp, const char *&cp_new)
std::string
to_string (double d, int prec)
{
if (std::isnan (d)) {
return nan_string;
} else if (std::isinf (d)) {
return d < 0 ? ninf_string : inf_string;
}
// For small values less than 1e-(prec) simply return "0" to avoid ugly values like "1.2321716e-14".
if (fabs (d) < pow (10.0, -prec)) {
return "0";
@@ -393,6 +442,12 @@ to_string (double d, int prec)
std::string
to_string (float d, int prec)
{
if (std::isnan (d)) {
return nan_string;
} else if (std::isinf (d)) {
return d < 0 ? ninf_string : inf_string;
}
// For small values less than 1e-(prec) simply return "0" to avoid ugly values like "1.2321716e-14".
if (fabs (d) < pow (10.0, -prec)) {
return "0";
@@ -813,6 +868,7 @@ from_string_numeric (const std::string &s, double &v, bool eval)
if (! *cp) {
throw tl::Exception (tl::to_string (tr ("Got empty string where a real number was expected")));
}
const char *cp_end = cp;
v = local_strtod (cp, cp_end);
while (safe_isspace (*cp_end)) {
+26 -3
View File
@@ -1057,15 +1057,38 @@ normalized_type (Variant::type type1, Variant::type type2)
static const double epsilon = 1e-13;
// NOTE: in order to be able to use Variant for std::map or std::set
// keys we have to establish a weak order. This means we need to
// consider NAN and INFINITY too.
static int numeric_class (double x)
{
if (std::isnan (x)) {
return 2;
} else {
return std::isinf (x) ? (x < 0 ? -1 : 1) : 0;
}
}
static inline bool fequal (double a, double b)
{
double avg = 0.5 * (fabs (a) + fabs (b));
return fabs (a - b) <= epsilon * avg;
if (numeric_class (a) != 0 || numeric_class (b) != 0) {
return numeric_class (a) == numeric_class (b);
} else {
double avg = 0.5 * (fabs (a) + fabs (b));
return fabs (a - b) <= epsilon * avg;
}
}
static inline bool fless (double a, double b)
{
return fequal (a, b) ? false : a < b;
if (fequal (a, b)) {
return false;
} else if (numeric_class (a) != 0 || numeric_class (b) != 0) {
return numeric_class (a) < numeric_class (b);
} else {
return a < b;
}
}
bool
+59
View File
@@ -589,3 +589,62 @@ TEST(15)
EXPECT_EQ (tl::to_upper_case ("nOrMaliI(\xc3\xa4\xc3\x84\xc3\xbc\xc3\x9c\xc3\xb6\xc3\x96\xc3\x9f-42\xc2\xb0+6\xe2\x82\xac)"), "NORMALII(\xc3\x84\xc3\x84\xc3\x9c\xc3\x9c\xc3\x96\xc3\x96\xc3\x9f-42\xc2\xb0+6\xe2\x82\xac)");
EXPECT_EQ (tl::to_lower_case ("nOrMaliI(\xc3\xa4\xc3\x84\xc3\xbc\xc3\x9c\xc3\xb6\xc3\x96\xc3\x9f-42\xc2\xb0+6\xe2\x82\xac)"), "normalii(\xc3\xa4\xc3\xa4\xc3\xbc\xc3\xbc\xc3\xb6\xc3\xb6\xc3\x9f-42\xc2\xb0+6\xe2\x82\xac)");
}
// Special numerical values
TEST(16)
{
EXPECT_EQ (tl::to_string (NAN), "nan");
EXPECT_EQ (tl::to_string (INFINITY), "inf");
EXPECT_EQ (tl::to_string (-INFINITY), "-inf");
EXPECT_EQ (tl::to_string ((float) NAN), "nan");
EXPECT_EQ (tl::to_string ((float) INFINITY), "inf");
EXPECT_EQ (tl::to_string ((float) -INFINITY), "-inf");
EXPECT_EQ (tl::micron_to_string (NAN), "nan");
EXPECT_EQ (tl::micron_to_string (INFINITY), "inf");
EXPECT_EQ (tl::micron_to_string (-INFINITY), "-inf");
EXPECT_EQ (tl::db_to_string (NAN), "nan");
EXPECT_EQ (tl::db_to_string (INFINITY), "inf");
EXPECT_EQ (tl::db_to_string (-INFINITY), "-inf");
double x = 0.0;
tl::from_string ("nan", x);
EXPECT_EQ (tl::to_string (x), "nan");
x = 0.0;
tl::from_string ("NaN", x);
EXPECT_EQ (tl::to_string (x), "nan");
x = 0.0;
tl::from_string ("inf", x);
EXPECT_EQ (tl::to_string (x), "inf");
x = 0.0;
tl::from_string ("INF", x);
EXPECT_EQ (tl::to_string (x), "inf");
x = 0.0;
tl::from_string ("-inf", x);
EXPECT_EQ (tl::to_string (x), "-inf");
x = 0.0;
tl::from_string ("-INF", x);
EXPECT_EQ (tl::to_string (x), "-inf");
std::string s;
tl::Extractor ex;
x = 0.0;
s = " inf nan\t -inf";
ex = tl::Extractor (s.c_str ());
EXPECT_EQ (ex.try_read (x), true);
EXPECT_EQ (tl::to_string (x), "inf");
EXPECT_EQ (ex.try_read (x), true);
EXPECT_EQ (tl::to_string (x), "nan");
EXPECT_EQ (ex.try_read (x), true);
EXPECT_EQ (tl::to_string (x), "-inf");
s = " Inf NaN\t -INF";
ex = tl::Extractor (s.c_str ());
EXPECT_EQ (ex.try_read (x), true);
EXPECT_EQ (tl::to_string (x), "inf");
EXPECT_EQ (ex.try_read (x), true);
EXPECT_EQ (tl::to_string (x), "nan");
EXPECT_EQ (ex.try_read (x), true);
EXPECT_EQ (tl::to_string (x), "-inf");
}
+106
View File
@@ -1090,4 +1090,110 @@ TEST(6)
EXPECT_EQ (tl::Variant (-0.1 * (1.0 + 1.1e-13)) < tl::Variant (0.1), true);
}
// special numeric values
TEST(7)
{
std::string s;
tl::Extractor ex;
tl::Variant v;
s = " ##\t 0.5";
ex = tl::Extractor (s.c_str ());
EXPECT_EQ (ex.try_read (v), true);
EXPECT_EQ (v.to_parsable_string (), "##0.5");
s = "## nan";
ex = tl::Extractor (s.c_str ());
EXPECT_EQ (ex.try_read (v), true);
EXPECT_EQ (v.to_parsable_string (), "##nan");
s = "## NaN";
ex = tl::Extractor (s.c_str ());
EXPECT_EQ (ex.try_read (v), true);
EXPECT_EQ (v.to_parsable_string (), "##nan");
s = "## inf";
ex = tl::Extractor (s.c_str ());
EXPECT_EQ (ex.try_read (v), true);
EXPECT_EQ (v.to_parsable_string (), "##inf");
s = "## Inf";
ex = tl::Extractor (s.c_str ());
EXPECT_EQ (ex.try_read (v), true);
EXPECT_EQ (v.to_parsable_string (), "##inf");
s = "## -inf";
ex = tl::Extractor (s.c_str ());
EXPECT_EQ (ex.try_read (v), true);
EXPECT_EQ (v.to_parsable_string (), "##-inf");
s = "## -Inf";
ex = tl::Extractor (s.c_str ());
EXPECT_EQ (ex.try_read (v), true);
EXPECT_EQ (v.to_parsable_string (), "##-inf");
v = tl::Variant ("nan");
v = tl::Variant (v.to_double ());
EXPECT_EQ (v.to_parsable_string (), "##nan");
EXPECT_EQ (v.to_string (), "nan");
v = tl::Variant ("Inf");
v = tl::Variant (v.to_double ());
EXPECT_EQ (v.to_parsable_string (), "##inf");
EXPECT_EQ (v.to_string (), "inf");
v = tl::Variant (INFINITY);
EXPECT_EQ (v.to_parsable_string (), "##inf");
EXPECT_EQ (v.to_string (), "inf");
v = tl::Variant (-INFINITY);
EXPECT_EQ (v.to_parsable_string (), "##-inf");
EXPECT_EQ (v.to_string (), "-inf");
tl::Variant vinf (INFINITY);
tl::Variant vninf (-INFINITY);
tl::Variant vnan (NAN);
tl::Variant vzero (0.0);
EXPECT_EQ (vninf == vninf, true);
EXPECT_EQ (vninf == vzero, false);
EXPECT_EQ (vninf == vinf, false);
EXPECT_EQ (vninf == vnan, false);
EXPECT_EQ (vninf < vninf, false);
EXPECT_EQ (vninf < vzero, true);
EXPECT_EQ (vninf < vinf, true);
EXPECT_EQ (vninf < vnan, true);
EXPECT_EQ (vzero == vninf, false);
EXPECT_EQ (vzero == vzero, true);
EXPECT_EQ (vzero == vinf, false);
EXPECT_EQ (vzero == vnan, false);
EXPECT_EQ (vzero < vninf, false);
EXPECT_EQ (vzero < vzero, false);
EXPECT_EQ (vzero < vinf, true);
EXPECT_EQ (vzero < vnan, true);
EXPECT_EQ (vinf == vninf, false);
EXPECT_EQ (vinf == vzero, false);
EXPECT_EQ (vinf == vinf, true);
EXPECT_EQ (vinf == vnan, false);
EXPECT_EQ (vinf < vninf, false);
EXPECT_EQ (vinf < vzero, false);
EXPECT_EQ (vinf < vinf, false);
EXPECT_EQ (vinf < vnan, true);
EXPECT_EQ (vnan == vninf, false);
EXPECT_EQ (vnan == vzero, false);
EXPECT_EQ (vnan == vinf, false);
EXPECT_EQ (vnan == vnan, true);
EXPECT_EQ (vnan < vninf, false);
EXPECT_EQ (vnan < vzero, false);
EXPECT_EQ (vnan < vinf, false);
EXPECT_EQ (vnan < vnan, false);
}
}