You are not logged in.

## #1 2012-09-09 13:09:14

zacariaz
Member
Registered: 2012-01-18
Posts: 492
Website

### [Solved] Bash and Floating point arithmetic

I didn't realize how troublesome floating point numbers can be until now.

What I want to do should be simple I dare say:

``properRounding( ( currentTime - downloadTime ) / ( dueTime - downloadTime ) * 100 )``

however best I've been able to achieve so far is this:

``echo "(\$currentTime-\$downloadTime)/(\$dueTime-\$downloadTime)*100" | bc -l``

Which prints the correct floating point value.
I've tried to put the result in a variable, but I must be doing it wrong as I get the most peculiar error. I can live without it, but it would make life easier.
As for the rounding, that is a must. I've read that if you remove the -l param from bc, then it will round, but in my case something goes wrong as I just get the value 0 in return and besides, concluded after a simple test, bc always rounds down as in integer division, which I can not use.

So of course I'll continue reading and hopefully someday arrive at a solution, but I would very much appreciate if someone could lend me a hand.

This after all is not just a learning experience, I'm trying to create something useful.

Best regards.

edit:
nb:
all variables are integers.

Last edited by zacariaz (2012-09-09 14:50:18)

Offline

## #2 2012-09-09 13:18:48

Procyon
Member
Registered: 2008-05-07
Posts: 1,819

### Re: [Solved] Bash and Floating point arithmetic

You'll indeed have to use bc, or dc.

You have to set the scale (e.g. 3k / scale=3) for both programs to show decimals:

``echo 3k 20000 19000 - 26000 29000 - / 100 \* f | dc``
``{ echo scale=3; echo "(20000 - 19000) / (26000 - 29000) * 100"; } | bc``

Offline

## #3 2012-09-09 13:28:23

zacariaz
Member
Registered: 2012-01-18
Posts: 492
Website

### Re: [Solved] Bash and Floating point arithmetic

Didn't know about dc, that I'll have to investigate as I prefer to use the standard tools.

'scale' however don't solve my problem, take for example:

``{ echo scale=n; 2 / 3"; } | bc``

for n 0 the result should be 1.
for 1 it should be .7
for to .67
etc.
the same goes when using dc of curse.

Offline

## #4 2012-09-09 13:53:58

Procyon
Member
Registered: 2008-05-07
Posts: 1,819

### Re: [Solved] Bash and Floating point arithmetic

Googling around a bit, I found you need to divide by 1. But in this case you also need to do the first division in high scale, and add 5 to the decimal place one before the one you want to round at.

``echo "scale=5; div=1/3; scale=0; (div+0.5)/1" | bc``
``echo "scale=5; div=2/3; scale=1; (div+0.05)/1" | bc``
``echo 5k 2 3 / 0k 0.5 + 1 / f | dc``
``echo 5k 2 3 / 1k 0.05 + 1 / f | dc``

Last edited by Procyon (2012-09-09 13:55:19)

Offline

## #5 2012-09-09 14:12:57

zacariaz
Member
Registered: 2012-01-18
Posts: 492
Website

### Re: [Solved] Bash and Floating point arithmetic

Procyon wrote:

Googling around a bit, I found you need to divide by 1. But in this case you also need to do the first division in high scale, and add 5 to the decimal place one before the one you want to round at.

``echo "scale=5; div=1/3; scale=0; (div+0.5)/1" | bc``
``echo "scale=5; div=2/3; scale=1; (div+0.05)/1" | bc``
``echo 5k 2 3 / 0k 0.5 + 1 / f | dc``
``echo 5k 2 3 / 1k 0.05 + 1 / f | dc``

I was about to reply how extremely confusing this is, when I suddenly realized that isn't the case at all.

Thanks once again.

Offline

## #6 2012-09-09 14:37:50

zacariaz
Member
Registered: 2012-01-18
Posts: 492
Website

### Re: [Solved] Bash and Floating point arithmetic

Well, I'm afraid I'm not quit out of the woods yet, especially with the 'dc' command.

``echo 3k \$currentTime \$downloadTime - \$dueTime \$downloadTime - / 100 \* 0.5 + f | dc``

The above prints the correct value with what looks to be a precision of 3, but really the precision is 1 with 2 trailing zeros.

Is it now I should mention that I'm not a fan of the stack?

In any case, I could probably get it to work with the 'bc' command, but as mentioned earlier, I would rather like to use the 'dc' command.

I have of course read the man page and tried to comprehend what you've posted, but frankly it hasn't helped much. Regardless of what I do, it just won't work.

edit:
Actually I may just have figured it out:

``echo 3k \$currentTime \$downloadTime - \$dueTime \$downloadTime - / 100 \* 0k 0.5 + 1 / f | dc``

It was the divide by one I forgot.

Also I just realized that dc comes with bc - doh.

edit:

Everything works!

Thanks a bunch.

Last edited by zacariaz (2012-09-09 14:49:55)

Offline

## #7 2012-09-09 14:51:09

Procyon
Member
Registered: 2008-05-07
Posts: 1,819

### Re: [Solved] Bash and Floating point arithmetic

The scale is 3 when you do the division.
2/3 = 0.666 (3 decimals)
100 * 0.666 = 66.600 (3 decimals)
Notice that I changed the scale halfway through, and the division by 1 at the end to apply the scale.

``echo 10k 2 3 / 100 \* 3k 0.0005 + 1/ f| dc``

Offline

## #8 2012-09-09 17:38:59

zacariaz
Member
Registered: 2012-01-18
Posts: 492
Website

### Re: [Solved] Bash and Floating point arithmetic

Just for the fun of it, here's my progress thus far... Well, there really isn't much more to do. The rest is a conky thing.

``````#!/bin/bash

# Variables from unitinfo.txt - date as unix timestamps.
dueTime="\$(date +%s -d "\$(grep 'Due time: ' ~/unitinfo.txt | cut -c11-)")"
if [ "\$1" = "end" ]
then echo \$dueTime
fi

if [ "\$1" = "start" ]
fi

progress="\$(grep 'Progress: ' ~/unitinfo.txt | cut -c11-12 | sed 's/ *\$//')"
if [ "\$1" = "prog1" ]
then echo \$progress
fi

# The rest

#progress valued 0-1 for use with conky proress bars
progress2=\$( echo 2k \$progress 100 / f | dc )
if [ "\$1" = "prog2" ]
then echo \$progress2
fi

# Current time - unix timestamp.
currentTime="\$(date +%s)"

# Remaining time - unix timestamp
remainingTime=\$(( dueTime-\$currentTime ))
if [ "\$1" = "remain" ]
then echo \$remainingTime
fi

# Elapsed time - unix timestamp
if [ "\$1" = "elap1" ]
then echo \$elapsedTime
fi

# Total amount of time available - unix timestamp
if [ "\$1" = "total" ]
then echo \$totalTime
fi

# How much time has elapsed in percent
progress3="\$(echo 3k \$elapsedTime \$totalTime / 100 \* 0k 0.5 + 1 / f | dc)"
if [ "\$1" = "elap2" ]
then echo \$progress3
fi

# Like the above bur 0-1
progress4=\$( echo 2k \$progress3 100 / f | dc )
if [ "\$1" = "elap3" ]
then echo \$progress4
fi

# In percent, expected completion vs \$dueTime - less than 100 is better.
expectedCompletion="\$( echo 3k \$elapsedTime 10000 \* \$progress / \$totalTime / 0k 0.5 + 1 / f | dc )"
if [ "\$1" = "exp1" ]
then echo \$expectedCompletion
fi

# Same as above, but unix timestamp
if [ "\$1" = "exp2" ]
then echo \$expectedCompletion2
fi

#efficiency
if [ "\$1" = "ef1" ]; then
if [ \$progress -lt \$progress3 ]
then echo "you're behind schedule."
elif [ \$progress -eq \$progress3 ]
then echo "You're right on schedule."
else
fi
fi
if [ "\$1" = "ef2" ]; then
if [ \$expectedCompletion -gt 100 ]
then echo "You're not going to make it!"
else
echo "You're going to make it!"
fi
fi``````

Offline