Closed hartwork closed 6 months ago
I'm thinking the fix here could be threefold:
scanf
— would be made to e.g. throw away any non-finite data points and store 0.0 insteaddraw_line
in function plot_values
could check against INT_MIN
and INT_MAX
and ceil/floor against these when passing, respecting the int
contractdraw_line
could check values l1
and l2
for safe upcoming use in formulas…
ph+1-l1
and ph+1-l2
andl2-l1
.The key idea here is to both protect against bad data entering the application and protect the draw layer from unpaintable results existing ttyplot's "math engine" in addition, to be waterproof.
@edgar-bonet @tenox7 what do you think?
@hartwork: This approach looks very defensive to me. I am fine defensive programming, but all this may not be strictly needed:
NANs are the real problem caught by the sanitizer, so yes, something has to be done about them. Either replace with zeroes, or skip these datapoints (if (isfinite(v1[i]) && isfinite(v2[i])) draw_line(...);
. The result should look the same.
If finite, the numbers being cast to int
are between 0.0
and ph
(the plot height), so they certainly lie between INT_MIN
and INT_MAX
.
The way l2
and l2
are computed, they also fit within the terminal height.
- If finite, the numbers being cast to
int
are between0.0
andph
(the plot height), so they certainly lie betweenINT_MIN
andINT_MAX
.
@edgar-bonet is that guaranteed? E.g. if min
and max
ever are equal then max-=min;
will make max
0 and then we divide by zero which should give infinity, multiplied by ph
is infinity again. I think we protect against them being equal — not sure if we cover all places — but min
and max
being very close — say 3.000000000001 and 3.000000000002 — can still shoot over the int
range when dividing by max
, no? That division is what worries me mostly.
@hartwork wrote:
if
min
andmax
ever are equal [...] then we divide by zero
Indeed, we should check whether this can ever happen, or whether this is already well protected against. It is not very clear to me, as there are some manipulations tweaking max
that depend on softmax
and harmin
...
but
min
andmax
being very close — say 3.000000000001 and 3.000000000002 — can still shoot over theint
range when dividing bymax
, no?
Nope. If min
and max
are close enough, IEEE-754 guarantees that computing their difference will give an exact result. This, by itself, is remarkable: not many floating point computations guarantee exact results. Then, if values1[i]
lies between min
and max
, the quantity (values1[i]-min)/(max-min)
will be between zero and one.
@edgar-bonet I've been experimenting with variants of code…
#include <assert.h>
#include <math.h>
#include <stdio.h>
int main() {
double max = 3.00000000000000000000000001;
double min = 3.00000000000000000000000002;
assert(max != min);
double numerator = 123.0;
double denominator = min - max;
assert(isfinite(denominator) && (denominator != 0.0));
double res = numerator / denominator;
printf("%f\n", res);
return 0;
}
…and it seems that once min
and max
have different values at runtime this no longer gets to inf
in practice. So my experiments seem to support your statement and that would mean that we only need part "one" out of my original threefold proposal. Cool!
To reproduce:
This is with GCC 12. Clang 15 would say…
…instead for the very same case.
PS: This was uncovered feeding ttyplot with
/dev/random
, briefly mentioned at https://github.com/tenox7/ttyplot/issues/78#issuecomment-1807175970 before but lacked a dedicated GitHub issue and a reproducer until now.