Settings

Theme

Show HN: Shellmath: Floating-point arithmetic directly in bash

github.com

30 points by clarity20 5 years ago · 18 comments

Reader

clarity20OP 5 years ago

Hello, all!

I'm proud to present shellmath, a floating-point calculator written entirely in bash.

Shellmath does not call out to any external programs -- calculators, text manipulators, or anything else -- and achieves execution speeds competitive with GNU awk and bc.

You can do floating-point math directly in the shell, and now you have a ready-made family of routines for doing so!

For skeptics ;-) I've included a demo that calculates 'e'. I've also posted a few words about the methodology, runtime efficiency, and a few bells and whistles on the README and in the project wiki.

I eagerly await your feedback!

Be good, and have a great day!

  • JNRowe 5 years ago

    This is awful¹, I love it!

    Thumbing through the code made me wonder what the zsh(my normal shell) implementation feels like, and I think it is fair to say your code is easier to wrap your head around than the documentation for floats in zsh.

    FWIW - and given that you've only shown Windows timings - on my Linux box hyperfine shows:

        Summary
          './faster_e_demo.sh 15' ran
            1.78 ± 0.16 times faster than './slower_e_demo.sh 15'
    
    I expected the speeds to be closer because of the differences with subshells on Windows and Linux, but the distance still surprised me.

    ¹ https://en.m.wiktionary.org/wiki/contranym - choose one

    • clarity20OP 5 years ago

      Thank you, I tried to make the code as readable as I could. And yes, since the *nix model for (sub)processes is cleaner than Cygwin-on-Windows it's kind of surprising that that gap is what it is.

  • pabs3 5 years ago

    Who does the name in the description refer to?

wahern 5 years ago

> Ad astra per aspera.

And yet reliance on Bash extensions rather than doing it in pure POSIX shell. ;)

A quick skim suggests the biggest convenience is using local variables to make recursion easier. But it's fairly easy to implement push and pop routines to turn global variables into a stack. You could even name push "local" and keep much the same syntax. Better would be an indirect call function which saves and restores a set of global "registers" across invocations, avoiding the need for explicit pops in each function.

Many of the pattern matching parts can be easily replaced with the canonical "fnmatch" shell implementation[1], which uses a case statement to provide in-shell fnmatch(3) emulation. If that's not powerful enough, expr supports regular expressions, though while usually a built-in it's not necessarily so. In the absence of regular expressions it's usually sufficient to use parameter expansion facilities to split strings and use simpler glob pattern matching on the components.

Still very cool. If I get bored one day I might try refactoring to make it pure POSIX shell.

Because Linux distributions (not to mention containers) are increasingly removing common shell utilities like bc, if this were extended to arbitrary precision arithmetic it might even be legitimately useful! For similar reasons I've implemented a base64 encoder and decoder using mostly pure POSIX shell--still depends on od(1) or dd(1) so it can do proper binary I/O, but the low-level coding is done using shell arithmetic and POSIX looping constructs.

[1] See http://www.etalabs.net/sh_tricks.html

  • clarity20OP 5 years ago

    I'm afraid I don't have the wherewithal to do this in the Bourne shell! An explicit call stack using those globals is a neat idea. Generally the [[ =~ ]] construct is my go-to for string parsing. Can you do everything you need with parameter expansion and string globbing alone (without turning on "extglob" swince that would be cheating)? Finally, is your encoder/decoder posted online?

  • smaudet 5 years ago

    "Because Linux distributions (not to mention containers) are increasingly removing common shell utilities like bc"

    Hmm that sounds fair, but if you can't do something simple like get bc why would you expect to be able to get this?

chubot 5 years ago

Very nice writeup!

Related: I got a couple mails about raytracers in pure bash. It looks like they use fixed point arithmetic in bash rather than floating point, which works well:

http://www.oilshell.org/blog/2021/01/audio-and-graphics.html...

https://github.com/aneeshdurg/bash-raytracer

https://www.bouledef.eu/~tleguern/articles/shell-fixedpoint/

  • clarity20OP 5 years ago

    Thank you, and a tip of the hat to them for doing square roots the hard way.

wodenokoto 5 years ago

> In the spirit of Bash, numerical overflow is silently ignored.

I love that!

082349872349872 5 years ago

so ... anyone have a floating-point library for sed(1)?

(this is a serious question. compare: http://sed.sourceforge.net/grabbag/tutorials/lookup_tables.t... and https://en.wikipedia.org/wiki/IBM_1620#Transferred_to_San_Jo... )

rurban 5 years ago

Why should I use `_shellmath_divide 1 $zero_factorial` over `bc 1 / $zero_factorial`?

It's imprecise and much more clunky to write. bc exists for decades already and has arbitrary precision.

  • wodenokoto 5 years ago

    I think shellmath exists because it could be done, rather than because it needed to be done.

    Also, `bc 1 / 2` causes an error (at least in bash 3.2 using bc version 1.06) because file 1 doesn't exist, so there's also that for a reason

phnofive 5 years ago

Well, anything’s better than multiplying the float by ten to the power of the number of decimals.

smaudet 5 years ago

Hmm. Cool?

I guess I don't expect my shell to do much, I reach for numpy or something when I want fancier math.

Not to knock, but what's the use case? I think I prefer bc - "10/(200.7-73.6)" is easy to look at, I don't mind the extra dependency for that.

Actually on that note what I do need to find is a proper calculator on Android, what I have found are computer algebra systems with arcane learning curves. Its surprising how bad the default calc is and nobody had made a popular good alt...

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection