[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: REQUEST - bash floating point math support
From: |
Robert Elz |
Subject: |
Re: REQUEST - bash floating point math support |
Date: |
Wed, 05 Jun 2024 21:57:26 +0700 |
Date: Wed, 5 Jun 2024 08:40:51 -0400
From: Zachary Santer <zsanter@gmail.com>
Message-ID:
<CABkLJUKDBPnOhYsb08u8QBL7axYottK_hdfN9yEk_mT-P8vp6A@mail.gmail.com>
| The magnification factor is handled as a
| fractional number - 1.5, 1.75, etc. So, to change the magnification
| factor by increments of 0.25 or 0.5, I had to print an expression into
| bc in a command substitution.
You don't need bc for that (and if you did, for scripts, I'd generally
recommend awk - you can easily write whole programs in that instead of just
expressions) all you need is scaled integers.
Treat everything as in units of .001 (scaled by 1000) and just do normal
arithmetic (given that you are never going to want to multiply or divide
magnification factors by each other - if you do, in some other example,
you need to work just a little harder to keep the scale known and sane).
Then when you're ready to pass the result to some program that is expecting
floating point input just:
printf %d.%.3d $(( val / 1000 )) $(( val % 1000 ))
and you're done. You can use whatever scale factor allows the integer
part to avoid overflows (which isn't a limit for most applications) and
gives sufficient precision to the fractional part.
Applications that really need floating point arithmetic are very rarely
going to be appropriate to write as a shell script, if you have one of
those, you really ought to be using a language (compiled, or interpreted)
which can handle it properly, and so you get access to all the maths
functions that floating point applications tend to want to use (and bc
has only a very small fraction of those in its library - awk even less).
Also note that to actually put floating support in the shell, more is
needed than just arithmetic, you also need floating comparisons in test
(or in bash, in [[ ) and a whole bunch more odds and ends that aren't
obvious until you need them, and they're just not there (like a mechanism
to convert floats back into integers again, controlling how rounding happens).
kre
ps: as a real example, where real floating arith is needed, the following
is a fragment from a script, which (when this part is encountered) has in
variable L an ordered list of floating point numbers (which happen to be
durations in seconds, with unspecified precision and range (ie: not integers),
but that explains some constants in the code). The objective is to
find the mean of the values from that list which are > 0.75 * and < 1.25 *
of the (approx) median value (that is, ignoring extreme values, high and low)
provided that most of the values are within that range. The output is that
average (mean) as an integer number of minutes, rounded to the nearest 5.
That is, to give a very rough approximation of what the typical duration is,
in units humans more easily deal with than thousands of seconds (to N decimal
places for some N I don't know or care about). If the result is big enough
that presenting it in hrs & mins is better, other code can make that happen
(that's simple integer arithmetic).
This isn't written for bash (hence uses no bash extensions) but there's
no reason I can think of why bash couldn't run it. The script is run
quite frequently (several times every few minutes) with entirely
acceptable performance (this particular fragment usually gets executed
30 to 40 times each execution of the script - that is, to deal with that
many different lists, the actual number varies).
unset IFS
set -- $( printf '%s\n' $L | sort -n )
N=$#
if test "$N" -eq 0
then
exit
fi
shift $(( (N - 1) / 2 ))
M=$1
printf '%s\n' $L | awk -v M="$M" '
BEGIN {
L = M * 0.75
U = M * 1.25
N=0
O=0
T=0
}
NR == 1 {
if ($1 < L || $1 > U) {
A = ($1 + 30) / 60
A = int( (A + 2.5) / 5 ) * 5
printf "x %.0f", A
exit
}
}
{
if ($1 < L || $1 > U ) {
O = O + 1
next
}
T = T + $1
N = N + 1
}
END {
if ( (N > 0 && N > O) ||
(N == 1 && O == 1) ) {
A = (T/N + 30) / 60
A = int( (A + 2.5) / 5 ) * 5
printf "%.0f", A
}
}
'
The rest of the script isn't relevant to the issue of floating point
arithmetic - the values in L are read from a simple database, not
calculated in this script.
The fragment above (and a little more preceding it, which is just bookkeeping,
so also omitted) is embedded in a command substitution, its output is
assigned to a variable, then used elsewhere in the script (note, that
value is an integer, so no floats needed where it is used).
pps: if you looked at the code, you might have noticed that if the first
entry in the list ($L) is out of range, it is handled differently - that's
intentional, the code to process the variable looks for that "x " and
does different things in that case (removing those chars, which could
have been anything not occurring in a simple integer). Also, when no
suitable value can be produced, it simply produces nothing - the remaining
code also handles that case. Also, the code the finds the median is
crude - but good enough for this, the sort just sorts the integer part
of each floating number, so 296.375 might come after 296.901 but that's
close enough whichever of those (if the median happens to be either of
them) is picked makes no significant difference in calculating the
bounds for discarding outliers, which is all it is used for. Similarly
the shift might find the actual median, or the adjacent value. Also
close enough for the purpose of the script. (And I know the final N>0
test before printing the average isn't useful, if N>O then we know N>0,
so we don't need the >0 test, just the >O test (do note the difference!)
I might remove that one day - but for here, that code is exactly what is
in the script, as it exists today.)
- Re: REQUEST - bash floating point math support, (continued)
- Re: REQUEST - bash floating point math support, Koichi Murase, 2024/06/17
- Re: REQUEST - bash floating point math support, Léa Gris, 2024/06/17
- Re: REQUEST - bash floating point math support, Zachary Santer, 2024/06/17
- Re: REQUEST - bash floating point math support, Chet Ramey, 2024/06/21
- Re: REQUEST - bash floating point math support, alex xmb sw ratchev, 2024/06/21
- Re: REQUEST - bash floating point math support, Chet Ramey, 2024/06/21
- Re: REQUEST - bash floating point math support, Saint Michael, 2024/06/21
- Re: REQUEST - bash floating point math support, alex xmb sw ratchev, 2024/06/21
- Re: REQUEST - bash floating point math support, Chet Ramey, 2024/06/21
- Re: REQUEST - bash floating point math support, Chet Ramey, 2024/06/12
- Re: REQUEST - bash floating point math support,
Robert Elz <=
- Re: REQUEST - bash floating point math support, Greg Wooledge, 2024/06/05
- Re: REQUEST - bash floating point math support, Léa Gris, 2024/06/06
- Re: REQUEST - bash floating point math support, Robert Elz, 2024/06/05
- Re: REQUEST - bash floating point math support, Saint Michael, 2024/06/05
- Re: REQUEST - bash floating point math support, Greg Wooledge, 2024/06/05
- Re: REQUEST - bash floating point math support, Saint Michael, 2024/06/05
- Re: REQUEST - bash floating point math support, Robert Elz, 2024/06/05
- Re: REQUEST - bash floating point math support, Zachary Santer, 2024/06/06