You are not logged in.

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

zacariaz
Member
From: Denmark
Registered: 2012-01-18
Posts: 539

[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)


I am a philosopher, of sorts, not a troll or an imbecile.
My apologies that this is not always obvious, despite my best efforts.

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
From: Denmark
Registered: 2012-01-18
Posts: 539

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.


I am a philosopher, of sorts, not a troll or an imbecile.
My apologies that this is not always obvious, despite my best efforts.

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
From: Denmark
Registered: 2012-01-18
Posts: 539

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.


I am a philosopher, of sorts, not a troll or an imbecile.
My apologies that this is not always obvious, despite my best efforts.

Offline

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

zacariaz
Member
From: Denmark
Registered: 2012-01-18
Posts: 539

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? wink

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)


I am a philosopher, of sorts, not a troll or an imbecile.
My apologies that this is not always obvious, despite my best efforts.

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
From: Denmark
Registered: 2012-01-18
Posts: 539

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

downloadTime="$(date +%s -d "$(grep 'Download time: ' ~/unitinfo.txt | cut -c16-)")"
if [ "$1" = "start" ]
  then echo $downloadTime
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
elapsedTime=$(( currentTime-downloadTime ))
if [ "$1" = "elap1" ]
  then echo $elapsedTime
fi

# Total amount of time available - unix timestamp
totalTime=$(( dueTime-downloadTime ))
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
expectedCompletion2=$(( downloadTime+(expectedCompletion*totalTime/100) ))
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
    echo "You're ahead of schedule."
  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

I am a philosopher, of sorts, not a troll or an imbecile.
My apologies that this is not always obvious, despite my best efforts.

Offline

Board footer

Powered by FluxBB