gavinhoward / bc

An implementation of the POSIX bc calculator with GNU extensions and dc, moved away from GitHub. Finished, but well-maintained.
https://git.gavinhoward.com/gavin/bc
Other
145 stars 29 forks source link

bc attempts to first calculate gigantic obase before erroring out #75

Closed mogando668 closed 6 months ago

mogando668 commented 6 months ago

Dear Mr. Howard,

For gigantic obases, like those below the line, even though they're clearly beyond the hard-coded obasemax of 2,147,483,647,bc appears to calculate the expression to full precision before erroring out, thus undermining any early exit criteria that ensure built-in named variables are within designated limits. I haven't tested against ibase but I suspect something similar would plague it, i.e. the right hand side expression is being calculated to full precision before any attempts to check them against caps.

The full zsh-based testing code and output are attached below.

Yours Sincerely Jason K

ps : I've noticed the same issue plagues both gnu-bc I've installed via Homebrew as well as the macOS built-in bc.

 2 ^ 8 ^  1 := 2 ^          8
 2 ^ 8 ^  2 := 2 ^         64
 2 ^ 8 ^  3 := 2 ^        512
 2 ^ 8 ^  4 := 2 ^       4096
 2 ^ 8 ^  5 := 2 ^      32768
 2 ^ 8 ^  6 := 2 ^     262144
 2 ^ 8 ^  7 := 2 ^    2097152
----------------------------
 2 ^ 8 ^  8 := 2 ^   16777216
 2 ^ 8 ^  9 := 2 ^  134217728
 2 ^ 8 ^ 10 := 2 ^ 1073741824
bc 1.07.1
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.

Darwin m1mx4CT 22.3.0 Darwin Kernel Version 22.3.0: Mon Jan 30 20:38:37 PST 2023; root:xnu-8792.81.3~2/RELEASE_ARM64_T6000 arm64

gbc is /usr/local/bin/gbc

BC_BASE_MAX     = 2147483647
BC_DIM_MAX      = 16777215
BC_SCALE_MAX    = 2147483647
BC_STRING_MAX   = 2147483647
MAX Exponent    = 9223372036854775807
Number of vars  = 32767

for __ in $( jot 10 ); do ( time ( printf 'obase=2^8^%d\0' "$__" | xargs -0 -n 1 -P 24 timeout -v --preserve-status --foreground 8 dash -c 'for __; do echo "$__" | gbc; done' _ ) ); echo "\f ----------\n\t finished 2^8^$__ with exit status $? ... \n --------"; done

( printf 'obase=2^8^%d\0' "$__" | xargs -0 -n 1 -P 24 timeout -v  --foregroun)  0.00s user 0.01s system 86% cpu 0.008 total

 ----------
     finished 2^8^1 with exit status 0 ... 
 --------
Runtime warning (func=(main), adr=13): obase too large, set to 2147483647
( printf 'obase=2^8^%d\0' "$__" | xargs -0 -n 1 -P 24 timeout -v  --foregroun)  0.00s user 0.01s system 58% cpu 0.012 total

 ----------
     finished 2^8^2 with exit status 0 ... 
 --------
Runtime warning (func=(main), adr=13): obase too large, set to 2147483647
( printf 'obase=2^8^%d\0' "$__" | xargs -0 -n 1 -P 24 timeout -v  --foregroun)  0.00s user 0.00s system 82% cpu 0.007 total

 ----------
     finished 2^8^3 with exit status 0 ... 
 --------
Runtime warning (func=(main), adr=13): obase too large, set to 2147483647
( printf 'obase=2^8^%d\0' "$__" | xargs -0 -n 1 -P 24 timeout -v  --foregroun)  0.00s user 0.00s system 84% cpu 0.008 total

 ----------
     finished 2^8^4 with exit status 0 ... 
 --------
Runtime warning (func=(main), adr=13): obase too large, set to 2147483647
( printf 'obase=2^8^%d\0' "$__" | xargs -0 -n 1 -P 24 timeout -v  --foregroun)  0.01s user 0.00s system 90% cpu 0.012 total

 ----------
     finished 2^8^5 with exit status 0 ... 
 --------
Runtime warning (func=(main), adr=13): obase too large, set to 2147483647
( printf 'obase=2^8^%d\0' "$__" | xargs -0 -n 1 -P 24 timeout -v  --foregroun)  0.14s user 0.01s system 98% cpu 0.143 total

 ----------
     finished 2^8^6 with exit status 0 ... 
 --------
Runtime warning (func=(main), adr=13): obase too large, set to 2147483647
( printf 'obase=2^8^%d\0' "$__" | xargs -0 -n 1 -P 24 timeout -v  --foregroun)  3.64s user 0.01s system 99% cpu 3.654 total

 ----------
     finished 2^8^7 with exit status 0 ... 
 --------
timeout: sending signal TERM to command ‘dash’
( printf 'obase=2^8^%d\0' "$__" | xargs -0 -n 1 -P 24 timeout -v  --foregroun)  0.00s user 0.00s system 0% cpu 8.009 total

 ----------
     finished 2^8^8 with exit status 0 ... 
 --------
timeout: sending signal TERM to command ‘dash’
( printf 'obase=2^8^%d\0' "$__" | xargs -0 -n 1 -P 24 timeout -v  --foregroun)  0.00s user 0.00s system 0% cpu 8.008 total

 ----------
     finished 2^8^9 with exit status 0 ... 
 --------
Runtime warning (func=(main), adr=13): obase too large, set to 2147483647
timeout: sending signal TERM to command ‘dash’
( printf 'obase=2^8^%d\0' "$__" | xargs -0 -n 1 -P 24 timeout -v  --foregroun)  0.00s user 0.01s system 0% cpu 8.011 total

 ----------
     finished 2^8^10 with exit status 0 ... 
 --------
gavinhoward commented 6 months ago

In general, this problem is unsolvable. It's a consequence of the limits of computing. (The technical term is "Turing-completeness.")

For example, to us it's obvious that 2^8^10 is out of range. But computers do not understand any "concept" of math, so they can't tell.

All they can do is execute the code that they are given.

You'd see an different execution time if you did this:

obase = 104438888141315250669175271071662438257996424904738378038423348328395390\
797155745684882681193499755834089010671443926283798757343818579360726323\
608785136527794595697654370999834036159013438371831442807001185594622637\
631883939771274567233468434458661749680790870580370407128404874011860911\
446797778359802900668693897688178778594690563019026094059957945343282346\
930302669644305902501597239986771421554169383555988529148631823791443449\
673408781187263949647510018904134900841706167509366833385055103297208826\
955076998361636941193301521379682583718809183365675122131849284636812555\
022599830041234478486259567449219461702380650591324561082573183538008760\
862210283427019769820231316901767800667519548507992163641937028537512478\
401490715913545998279051339961155179427110683113409058427288427979155484\
978295432353451706522326906139490598769300212296339568778287894844061600\
741294567491982305057164237715481632138063104590291613692670834285644073\
044789997190178146576347322385026725305989979599609079946920177462481771\
844986745565925017832907047311943316555080756822184657174637329688491281\
952031745700244092661691087414838507841192980452298185733897764810312608\
590300130241346718972667321649151113160292078173803343609024380470834040\
3154190336

instead of this:

obase = 2^8^4

Again, the computer has no idea that 2^8^4 cannot work until it calculates it. We can see it by eye ("8^4 is definitely greater than 64, and 2^64 is the max"), but the computer doesn't think that like. It sees:

constant 2
constant 8
constant 4
x = exponentiation 8 4
exponentiation 2 x

I hope this makes sense.