3

I currently use the following to compute the following at the bash command line with commas every three digits:

printf "%'3.f\n" $(((29246636)*4096))
119,794,221,056

The equation needs to start with $(( and also end with )). The Linux Command Line reference has more information about formatting text.

I also have that:

printf "%'.3f \n" $(((31200310)*4096))
127,796,469,760.000

I want to make sure that when there is a division that there is no fractional part left over.

Is the best way to accomplish this as:

printf "%'.3f\n" $(((((125046784)*512)/1024)/64))
976,928.000

But this is problematic. The last three digits always appear to be .000 and fractional parts do not seem to be recorded after the decimal point. It is fine as long as the division is guaranteed to be even with no fractional leftover. But if that is not the case, the answer seem to be only the decimal part with zeros to the right of the decimal point and no indication that there is a fractional part.

I understand that this question seems simple. But for now I end up using the Linux Ubuntu calculator instead of the simpler command line because of this potential trap.

Thank you all for any insight to make this calculation with division and a fractional remainder easier on the command line. And, yes, I did try 64.0 instead of 64 at which point I get a syntax error. Perhaps something more sophisticated needs to be used.

I found this reference: "How to print the first three digits after the decimal point of a floating number?"

But I still do not understand how this might be applied in this case to get the desired result. Any help would be appreciated.

After reading the suggestions, I came up with the following bash script that I store in /usr/local/sbin/quotrem so that I can access it from any command prompt. This script is lacking still because I have to download it each time I want to use it on a different Ubuntu machine. Plus, it is not a desktop icon that I can just double click to input the required values and get the answer. Perhaps others can make suggestions how to improve there or in another way.

#!/bin/bash
#This quoterm script takes two arguments: quoterm a b
#Both a and b are integers and the result is a / b with a remainder of a % b

 a=$1;
 b=$2;

 printf "a=%'3.f " $((a))
 printf "b=%'3.f " $((b))
 printf "a/b = %'3.f " $((a / b))
 printf "with the remainder of %'3.f " $((a % b))
 printf "\n"

Note that the use of /usr/local/sbin, I found it from this link: 3.16. /sbin : System binaries. I also found the scripting information from this link: The Linux Command Line: 32 – Positional Parameters - Accessing The Command Line

2
  • 3
    Does this answer your question? Can I use fractions/decimals in a bash script?
    – mook765
    Jan 29 at 14:08
  • Yes. It answers the direct solution for the problem of getting the decimal digits to the right. However, as pointed out by @steeldriver, the question may be similar to an XY Problem. That is that the really useful answer goes above and beyond the question. I need to use this for telling how many 64 K blocks I need to copy with the dd utility. If there is a fraction left over, I need to add one more block to copy so that I do not leave anything left over. Feb 9 at 14:29

4 Answers 4

11

Your question appears to be describing an XY problem

You want to know if two integers are divisible, and you are trying to do that by performing a floating point division, and checking the value of the digits after the decimal point in the result. As you have found out, bash does not natively support floating point arithmetic (although other shells, including zsh and ksh, do).

However bash does provide a remainder operator, %, that allows you to check divisibility directly:

$ (( (125046784*512) % (1024*64) == 0 )) && echo 'divisible' || echo 'not divisible'
divisible
$ 
$ (( (125046784*512) % (1024*63) == 0 )) && echo 'divisible' || echo 'not divisible'
not divisible

If you want to evaluate the quotient and remainder rather than just test for divisibility, you can combine / (integer division) and % (remainder) - you could even put it in a little shell function:

quotrem () 
{ 
    local int a=$1;
    local int b=$2;
    printf "%'d %'d\n" $(( a / b )) $(( a % b ))
}

Then

$ quotrem 511 64
7 63

or

$ quotrem $(( 125046784*512/1024 )) 64
976,928 0
3
  • This seems to address part of the solution -- namely identifying if there is a remainder without falling into a trap of thinking that there is no remainder when there is. In this application, the input is how many sectors. And the output is how many 64K byte blocks there are, and whether there is a remainder. Maybe a bash script with an input of how many sectors and an output of both how many 64K blocks there are and how much of a remainder there is would solve the problem. Maybe there is a better approach. What do you think? Jan 31 at 8:11
  • @StephenElliott yes you can combine integer division / and remainder % to find the quotient and remainder - see edit Jan 31 at 18:16
  • I think your approach of writing a script with the remainder is the best, because one can never be certain that there are not some small remainder that with floating point operations miss the radar screen. Feb 9 at 14:04
6

Since you know printf understands floats, I guess there is some space for maneuvering in your case ... bash doesn't do floats ... that's why the following will not be accurate and the usage of printf's float specifier is just a waste like so:

$ printf '%.3f\n' "$((129/64))"
2.000

But if you anticipate and bring any expected float results to be integers by e.g. multiplying by 1000 since you want three digits after the decimal mark like so:

$ printf '%.3f\n' "$((1000*129/64))"
2015.000

Then you get all your numbers but with the decimal mark moved three digits to the right from its correct position ... So, move it back(see mantissa/exponent) like so:

$ printf '%.3f\n' "$((1000*129/64))e-3"
2.015

And you get what you want.

Additionally, it might be worth noting that the printf's float specifier rounds the last number after the decimal mark when digits are shortened ... Therefore, as noted by @Paul_Pedant, it's a good idea to benefit from this behavior to get the numbers rounded more properly/accurately by adding an extra digit and returning the decimal mark an extra digit accordingly like so:

$ printf '%.3f\n' "$((10000*129/64))e-4"
2.016

Notice: This is a simple example to show you how it's done but it might not be that simple in complex arithmetic operations ... So, do your homework to know what arithmetic operators take precedence first and multiply by 1000 in the correct sequence/position.

2
  • 2
    Very elegant. You could even make it round properly by sneaking in a guard digit or two. Jan 30 at 10:14
  • Good point ... Thanks @Paul_Pedant :-)
    – Raffa
    Jan 30 at 15:24
3

You can use command line utilities like bc to perform floating point arithmetic and format the output to display only the first 3 digits after the period. For example, to multiply 2.5 by 3.6 and display only the first 3 digits after the period, you can use the following command:

echo "scale=3; 2.5 * 3.6" | bc

This command uses echo to pass the string "scale=3; 2.5 * 3.6" to the bc command, which interprets it as a floating point arithmetic expression. The scale command sets the number of digits after the decimal point to 3, and the multiplication is performed using the * operator. The output will be the first three digits after the period.

Similarly for Division,

echo "scale=3; 2.5 / 3.6" | bc

This will output the first 3 digits after the period of the division of 2.5 by 3.6

1
  • 1
    Does not work for me (Linux Mint). Your first example returns 9.00 - 2 decimal digits. echo "scale=3; 23.7777 * 3.6555" | bc returns 86.9193 - 4 digits and also bad rounding. scale seems to deal with accuracy of intermediate results, not with display format. Jan 30 at 20:41
1

According to the bash man page under ^ARITHMETIC EVALUATION only fixed width integers are supported in the (( ... )) syntax, so your best option is to use an external tool (such as bc, as in the other answer).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.