From d5b28df33a03b2dc084a76f42a4efb0b9e7b22ab Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 15 May 2025 15:27:24 -0500 Subject: [PATCH] scripts: Fixed excessive rounding when writing floats to csv/json files This adds __csv__ methods to all Csv* classes to indicate how to write csv/json output, and adopts Python's default float repr. As a plus, this also lets us use "inf" for infinity in csv/json files, avoiding potential unicode issues. Before this we were reusing __str__ for both table rendering and csv/json writing, which rounded to a single decimal digit! This made float output pretty much useless outside of trivial cases. --- Note Python apparently does some of its own rounding (1/10 -> 0.1?), so the result may still not be round-trippable, but this is probably fine for our somewhat hack-infested csv scripts. --- scripts/code.py | 12 ++++++++++-- scripts/cov.py | 15 +++++++++++++-- scripts/csv.py | 23 +++++++++++++++++++++-- scripts/ctx.py | 12 ++++++++++-- scripts/data.py | 12 ++++++++++-- scripts/perf.py | 12 ++++++++++-- scripts/perfbd.py | 12 ++++++++++-- scripts/stack.py | 12 ++++++++++-- scripts/structs.py | 12 ++++++++++-- 9 files changed, 104 insertions(+), 18 deletions(-) diff --git a/scripts/code.py b/scripts/code.py index 1caf27b7..d615e5c9 100755 --- a/scripts/code.py +++ b/scripts/code.py @@ -66,6 +66,14 @@ class CsvInt(co.namedtuple('CsvInt', 'a')): else: return str(self.a) + def __csv__(self): + if self.a == mt.inf: + return 'inf' + elif self.a == -mt.inf: + return '-inf' + else: + return repr(self.a) + def __bool__(self): return bool(self.a) @@ -992,7 +1000,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None}) @@ -1008,7 +1016,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None} | ({Result._children: jsonify( diff --git a/scripts/cov.py b/scripts/cov.py index a85582c5..9701062b 100755 --- a/scripts/cov.py +++ b/scripts/cov.py @@ -68,6 +68,14 @@ class CsvInt(co.namedtuple('CsvInt', 'a')): else: return str(self.a) + def __csv__(self): + if self.a == mt.inf: + return 'inf' + elif self.a == -mt.inf: + return '-inf' + else: + return repr(self.a) + def __bool__(self): return bool(self.a) @@ -156,6 +164,9 @@ class CsvFrac(co.namedtuple('CsvFrac', 'a,b')): def __str__(self): return '%s/%s' % (self.a, self.b) + def __csv__(self): + return '%s/%s' % (self.a.__csv__(), self.b.__csv__()) + def __bool__(self): return bool(self.a) @@ -852,7 +863,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None}) @@ -868,7 +879,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None} | ({Result._children: jsonify( diff --git a/scripts/csv.py b/scripts/csv.py index 09950f21..bfe5116b 100755 --- a/scripts/csv.py +++ b/scripts/csv.py @@ -59,6 +59,14 @@ class CsvInt(co.namedtuple('CsvInt', 'a')): else: return str(self.a) + def __csv__(self): + if self.a == mt.inf: + return 'inf' + elif self.a == -mt.inf: + return '-inf' + else: + return repr(self.a) + def __bool__(self): return bool(self.a) @@ -161,6 +169,14 @@ class CsvFloat(co.namedtuple('CsvFloat', 'a')): else: return '%.1f' % self.a + def __csv__(self): + if self.a == mt.inf: + return 'inf' + elif self.a == -mt.inf: + return '-inf' + else: + return repr(self.a) + def __bool__(self): return bool(self.a) @@ -248,6 +264,9 @@ class CsvFrac(co.namedtuple('CsvFrac', 'a,b')): def __str__(self): return '%s/%s' % (self.a, self.b) + def __csv__(self): + return '%s/%s' % (self.a.__csv__(), self.b.__csv__()) + def __bool__(self): return bool(self.a) @@ -2126,7 +2145,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None}) @@ -2142,7 +2161,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None} | ({Result._children: jsonify( diff --git a/scripts/ctx.py b/scripts/ctx.py index 7e0acad0..760cca05 100755 --- a/scripts/ctx.py +++ b/scripts/ctx.py @@ -62,6 +62,14 @@ class CsvInt(co.namedtuple('CsvInt', 'a')): else: return str(self.a) + def __csv__(self): + if self.a == mt.inf: + return 'inf' + elif self.a == -mt.inf: + return '-inf' + else: + return repr(self.a) + def __bool__(self): return bool(self.a) @@ -1252,7 +1260,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None}) @@ -1268,7 +1276,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None} | ({Result._children: jsonify( diff --git a/scripts/data.py b/scripts/data.py index 91594bd9..d9f78799 100755 --- a/scripts/data.py +++ b/scripts/data.py @@ -66,6 +66,14 @@ class CsvInt(co.namedtuple('CsvInt', 'a')): else: return str(self.a) + def __csv__(self): + if self.a == mt.inf: + return 'inf' + elif self.a == -mt.inf: + return '-inf' + else: + return repr(self.a) + def __bool__(self): return bool(self.a) @@ -992,7 +1000,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None}) @@ -1008,7 +1016,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None} | ({Result._children: jsonify( diff --git a/scripts/perf.py b/scripts/perf.py index c99dd1f4..d3754337 100755 --- a/scripts/perf.py +++ b/scripts/perf.py @@ -76,6 +76,14 @@ class CsvInt(co.namedtuple('CsvInt', 'a')): else: return str(self.a) + def __csv__(self): + if self.a == mt.inf: + return 'inf' + elif self.a == -mt.inf: + return '-inf' + else: + return repr(self.a) + def __bool__(self): return bool(self.a) @@ -1353,7 +1361,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None}) @@ -1369,7 +1377,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None} | ({Result._children: jsonify( diff --git a/scripts/perfbd.py b/scripts/perfbd.py index d496710d..d714ba98 100755 --- a/scripts/perfbd.py +++ b/scripts/perfbd.py @@ -67,6 +67,14 @@ class CsvInt(co.namedtuple('CsvInt', 'a')): else: return str(self.a) + def __csv__(self): + if self.a == mt.inf: + return 'inf' + elif self.a == -mt.inf: + return '-inf' + else: + return repr(self.a) + def __bool__(self): return bool(self.a) @@ -1327,7 +1335,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None}) @@ -1343,7 +1351,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None} | ({Result._children: jsonify( diff --git a/scripts/stack.py b/scripts/stack.py index 38329e37..c857f9fe 100755 --- a/scripts/stack.py +++ b/scripts/stack.py @@ -62,6 +62,14 @@ class CsvInt(co.namedtuple('CsvInt', 'a')): else: return str(self.a) + def __csv__(self): + if self.a == mt.inf: + return 'inf' + elif self.a == -mt.inf: + return '-inf' + else: + return repr(self.a) + def __bool__(self): return bool(self.a) @@ -995,7 +1003,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None}) @@ -1011,7 +1019,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None} | ({Result._children: jsonify( diff --git a/scripts/structs.py b/scripts/structs.py index 56a16d6f..2b19b47b 100755 --- a/scripts/structs.py +++ b/scripts/structs.py @@ -62,6 +62,14 @@ class CsvInt(co.namedtuple('CsvInt', 'a')): else: return str(self.a) + def __csv__(self): + if self.a == mt.inf: + return 'inf' + elif self.a == -mt.inf: + return '-inf' + else: + return repr(self.a) + def __bool__(self): return bool(self.a) @@ -1133,7 +1141,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None}) @@ -1149,7 +1157,7 @@ def write_csv(path, Result, results, *, {k: getattr(r, k) for k in by if getattr(r, k) is not None} - | {prefix+k: str(getattr(r, k)) + | {prefix+k: getattr(r, k).__csv__() for k in fields if getattr(r, k) is not None} | ({Result._children: jsonify(