59

How would I do something like:

ceiling(N/500)

N representing a number.

But in a linux Bash script

Zombo
  • 1
  • 62
  • 391
  • 407
Mint
  • 14,388
  • 30
  • 76
  • 108
  • A solution for the Bash is given at Unix & Linux: [Convert a float to the next integer up as opposed to the nearest](https://unix.stackexchange.com/questions/168476/convert-a-float-to-the-next-integer-up-as-opposed-to-the-nearest) – Frank Breitling Jul 01 '16 at 14:50
  • Beware: All answers here that are based on the shell arithmetic expansion `$(( ... ))` fail when the operands are already floating point numbers. E.g. `echo $((1.1))` yields: `-bash: 1.1: syntax error: invalid arithmetic operator (error token is ".1")` before even attempting any division. – Irfy Jan 11 '23 at 18:12

16 Answers16

115

Why use external script languages? You get floor by default. To get ceil, do

$ divide=8; by=3; (( result=(divide+by-1)/by )); echo $result
3
$ divide=9; by=3; (( result=(divide+by-1)/by )); echo $result
3
$ divide=10; by=3; (( result=(divide+by-1)/by )); echo $result
4
$ divide=11; by=3; (( result=(divide+by-1)/by )); echo $result
4
$ divide=12; by=3; (( result=(divide+by-1)/by )); echo $result
4
$ divide=13; by=3; (( result=(divide+by-1)/by )); echo $result
5
....

To take negative numbers into account you can beef it up a bit. Probably cleaner ways out there but for starters

$ divide=-10; by=10; neg=; if [ $divide -lt 0 ]; then (( divide=-divide )); neg=1; fi; (( result=(divide+by-1)/by )); if [ $neg ]; then (( result=-result )); fi; echo $result
-1

$ divide=10; by=10; neg=; if [ $divide -lt 0 ]; then (( divide=-divide )); neg=1; fi; (( result=(divide+by-1)/by )); if [ $neg ]; then (( result=-result )); fi; echo $result
1

(Edited to switch let ... to (( ... )).)

Kalle
  • 13,186
  • 7
  • 61
  • 76
19

Call out to a scripting language with a ceil function. Given $NUMBER:

python -c "from math import ceil; print ceil($NUMBER/500.0)"

or

perl -w -e "use POSIX; print ceil($NUMBER/500.0), qq{\n}"
Josh McFadden
  • 394
  • 3
  • 9
  • 1
    In Perl, wouldn't you use: `ceil($ARGV[0]/$ARGV[1])` to use the two script arguments? And then you'd use single quotes around the script. Or, using double quotes you can let the shell substitute its $1, $2. – Jonathan Leffler Mar 07 '10 at 03:53
  • Sure, yeah, perl -w -e 'use POSIX; print ceil($ARGV[0]/$ARGV[1]), qq{\n}' $N 500 is fine too, etc. TMTOWTDI. The important bit is using a standards-based ceil implementation. – Josh McFadden Mar 07 '10 at 04:11
  • Code tag fail? I'm new here. :( – Josh McFadden Mar 07 '10 at 04:12
  • Haha, yeah you can't code code in the comments :( but doesn't matter, I prefer to use Python anyway :) Thanks for the code! – Mint Mar 07 '10 at 04:20
  • 5
    The language requested was bash, and therefore should be a bash shell script that doesn't require the use of any other language. I do not believe that this should be the top answer. – DCurro Mar 21 '16 at 14:53
  • @DCurro, fixed, though at the time I asked the question there was no pure bash solution, so I picked the one that worked best for me. – Mint May 24 '21 at 00:54
12

Here's a solution using bc (which should be installed just about everywhere):

ceiling_divide() {
  ceiling_result=`echo "($1 + $2 - 1)/$2" | bc`
}

Here's another purely in bash:

# Call it with two numbers.
# It has no error checking.
# It places the result in a global since return() will sometimes truncate at 255.

# Short form from comments (thanks: Jonathan Leffler)
ceiling_divide() {
  ceiling_result=$((($1+$2-1)/$2))
}

# Long drawn out form.
ceiling_divide() {
  # Normal integer divide.
  ceiling_result=$(($1/$2))
  # If there is any remainder...
  if [ $(($1%$2)) -gt 0 ]; then
    # rount up to the next integer
    ceiling_result=$((ceiling_result + 1))
  fi
  # debugging
  # echo $ceiling_result
}
Harvey
  • 5,703
  • 1
  • 32
  • 41
  • 3
    You can simplify that to remove the conditional stuff: `ceiling_result=$((($1+$2-1)/$2))` – Jonathan Leffler Mar 07 '10 at 03:50
  • @1ch1g0 He said in Bash. There's no floating point in bash. His example showed integers as well and he wants an integer result. Anyway, I added a solution that uses bc. It'll handle the floating point numbers. – Harvey Mar 07 '10 at 04:31
6

You can use awk

#!/bin/bash
number="$1"
divisor="$2"
ceiling() {
  awk -vnumber="$number" -vdiv="$divisor" '
  function ceiling(x){return x%1 ? int(x)+1 : x}
  BEGIN{ print ceiling(number/div) }'
}
ceiling

output

$ ./shell.sh 1.234 500
1

Or if there's a choice, you can use a better shell that does floating point, eg Zsh

integer ceiling_result
ceiling_divide() {
  ceiling_result=$(($1/$2))
  echo $((ceiling_result+1))
}

ceiling_divide 1.234 500
Zombo
  • 1
  • 62
  • 391
  • 407
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
4

Expanding a bit on Kalle's great answer, here's the algorithm nicely packed in a function:

ceildiv() {
    local num=$1
    local div=$2
    echo $(( (num + div - 1) / div ))
}

or as a one-liner:

ceildiv(){ echo $((($1+$2-1)/$2)); }

If you want to get fancy, you could use a more robust version validates input to check if they're numerical, also handles negative numbers:

ceildiv() {
    local num=${1:-0}
    local div=${2:-1}
    if ! ((div)); then
        return 1
    fi
    if ((num >= 0)); then
        echo $(( (num + div - 1) / div ))
    else
        echo $(( -(-num + div - 1) / div ))
    fi
}

This uses a "fake" ceil for negative numbers, to the highest absolute integer, ie, -10 / 3 = -4 and not -3 as it should, as -3 > -4. If you want a "true" ceil, use $(( num / div )) instead after the else

And then use it like:

$ ceildiv 10 3
4
$ ceildiv 501 500
2
$ ceildiv 0 3
0
$ ceildiv -10 1
-10
$ ceildiv -10 3
-4
Community
  • 1
  • 1
MestreLion
  • 12,698
  • 8
  • 66
  • 57
4

Mathematically, the function of ceiling can be define with floor, ceiling(x) = -floor(-x). And, floor is the default when converting a positive float to integer.

if [ $N -gt 0 ]; then expr 1 - $(expr $(expr 1 - $N) / 500); else expr $N / 500; fi

Ref. https://en.wikipedia.org/wiki/Floor_and_ceiling_functions

Frank R.
  • 1,732
  • 18
  • 21
  • 1
    While this code snippet may solve the problem, it doesn't explain why or how it answers the question. Please include an explanation for your code, as that really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion – Balagurunathan Marimuthu Sep 25 '17 at 06:02
  • Thanks for the tips! – Frank R. Sep 25 '17 at 07:05
  • 1
    Not correct when the number is a whole number ... floor (500 + 1) = floor (501) = 501 != ceiling (500) – Martin Janiczek Nov 30 '17 at 09:37
  • 1
    @MartinJaniczek Thank you for pointing out the error. After reviewing the definition of ceiling and floor, my answer get updated. – Frank R. Nov 30 '17 at 11:52
3

You can use jq if you have it installed. It's "sed for JSON", but I find it surprisingly handy for simple tasks like this too.

Examples:

$ echo 10.001 | jq '.|ceil'
11

$ jq -n '-10.001 | ceil'
-10
Xiangming Hu
  • 185
  • 2
  • 7
2
Floor () {
  DIVIDEND=${1}
  DIVISOR=${2}
  RESULT=$(( ( ${DIVIDEND} - ( ${DIVIDEND} % ${DIVISOR}) )/${DIVISOR} ))
  echo ${RESULT}
}
R=$( Floor 8 3 )
echo ${R}

Ceiling () {
  DIVIDEND=${1}
  DIVISOR=${2}
  $(( ( ( ${DIVIDEND} - ( ${DIVIDEND} % ${DIVISOR}) )/${DIVISOR} ) + 1 ))
  echo ${RESULT}
}
R=$( Ceiling 8 3 )
echo ${R}
Zombo
  • 1
  • 62
  • 391
  • 407
bsaldivar
  • 175
  • 2
  • 6
1

If you have a string representation of a decimal number, bash does support ceiling using printf function like this:

$ printf %.4f 0.12345
0.1235

But if you need to do some math using decimals, you can use bc -l that by default scales to 20 decimals, then use the result with printf to round it.

printf %.3f $(echo '(5+50*3/20 + (19*2)/7 )' | bc -l)
17.929
ton
  • 3,827
  • 1
  • 42
  • 40
0

This is a simple solution using Awk:

If you want the ceil of ($a/$b) use

echo "$a $b" | awk '{print int( ($1/$2) + 1 )}'

and the floor use

echo "$a $b" | awk '{print int($1/$2)}'

Note that I just echo the dividend '$a' as the first field of the line to awk and the divisor '$b' as the second.

  • 6
    int( ($1/$2) + 1 ) != ceil($1/$2) if $1/$2 is integer! – Imp May 24 '12 at 03:22
  • 4
    This is wrong. Your ceil method will return the wrong results for divisions with no remainder. For example 2/2 is one (and no remainder) and ceil(1) should return 1. Your method returns 2. – StFS Apr 07 '14 at 11:16
0

Some more concise Awk logic

awk '
function ceil(ip) {
  print ip%1 ? int(ip)+1 : ip
}  
BEGIN {
  ceil(1000/500)
  ceil(1001/500)
}
'

Result

2
3
Zombo
  • 1
  • 62
  • 391
  • 407
0

Some more concise Awk logic

awk '
function ceil(ip) {
  print ip%1 ? int(ip)+1 : ip
}  
BEGIN {
  ceil(1000/500)
  ceil(1001/500)
}
'

Result

2
3
Zombo
  • 1
  • 62
  • 391
  • 407
0

This function wont't add 1, if the division returns a non-floating number.

function ceiling {
    DIVIDEND=${1}
    DIVISOR=${2}
    if [ $(( DIVIDEND % DIVISOR )) -gt 0 ]; then
            RESULT=$(( ( ( $DIVIDEND - ( $DIVIDEND % $DIVISOR ) ) / $DIVISOR ) + 1 ))
    else
            RESULT=$(( $DIVIDEND / $DIVISOR ))
    fi
    echo $RESULT
}

Use it like this:

echo $( ceiling 100 33 )
> 4
Regaddi
  • 94
  • 6
0

Using the gorgeous 'printf' 1 will round up to the next integer

printf %.0f $float
or
printf %.0f `your calculation formula`
or
printf %.0f $(your calculation formula)

ref: how to remove decimal from a variable?

splaisan
  • 845
  • 6
  • 22
0

Without specifying any function, we can use the following awk script:

echo x y | awk '{ r=$1 % $2; q=$1/y; if (r != 0) q=int(q+1); print q}'

Not sure this one get any logical error or not. Please correct.

Kemin Zhou
  • 6,264
  • 2
  • 48
  • 56
0

If you are already familiar with the Python library, then rather than learn bc, you might want to define this bash function:

pc () { pyexpr="from math import *; print($@)"; python -c "$pyexpr"; }

Then:

pc "ceil(3/4)"
1

but also any valid python expression works:

pc pi / 4
0.7853981633974483

pc "'\n'.join(['Pythagoras said that %3.2f^2 + %3.2f^2 is always %3.2f'
    % (sin(ai), cos(ai), sin(ai)**2 + cos(ai)**2)
    for ai in [pi / 4 * k for k in range(8)]])"
Pythagoras said that 0.00^2 + 1.00^2 is always 1.00
Pythagoras said that 0.71^2 + 0.71^2 is always 1.00
Pythagoras said that 1.00^2 + 0.00^2 is always 1.00
Pythagoras said that 0.71^2 + -0.71^2 is always 1.00
Pythagoras said that 0.00^2 + -1.00^2 is always 1.00
Pythagoras said that -0.71^2 + -0.71^2 is always 1.00
Pythagoras said that -1.00^2 + -0.00^2 is always 1.00
Pythagoras said that -0.71^2 + 0.71^2 is always 1.00
Leo
  • 2,775
  • 27
  • 29