A fixed point basic calculator for DASH.


Login or Register for Dates, Times and to Reply

 
Thread Tools Search this Thread
# 1  
A fixed point basic calculator for DASH.

This exercise has taught me a lot about POSIX, dash and their limits.

I decided to experiment with fixed point arithmetic using dash as the shell.
If you want to test it then change the shebang to your directory where 'dash' is or use 'bash' instead.

This was one of the hardest things I have done and it took me around 24 hours to just get addition and subtraction working with an accuracy of nine decimal places for calculation results inside + or - 1,000,000.000000000 and around 5 decimal places for the working limits of 64 bit integer maths of + or - 9223,372,036.85477[????].
I haven't done multiplication and division yet but have checked from the command line that these work and are easy enough now that I have this building block.
It will have bugs and limitations but for small scale quick fixed point calculations this seems to work fine at the moment.
As I once uploaded here, "Is there anything the UNIX shell can't do?" and the script is not that big.

This requires NO external command help to work, it is pure dash shell coding.
I will be finishing the multiplication and division whilst away next week.
Code:
#!/usr/local/bin/dash
#
#!/bin/bash
#
# 'calc' - A basic calculator for average sized numbers.
#
# OSX 10.13.6, default bash terminal.
# Another exercise in futility by B.Walker.
# Public Domain.
# Accurate enough to nine decimal places.
# USAGE: calc NUMBER1 +|-|*|/ NUMBER2
#
# In 64 bit mode.
# Total upper limit = +9223372036.85477; 64 bit signed integer = 9223372036854775807.
# Total lower limit = -9223372036.85477; 64 bit signed integer = 9223372036854775808.
# This allows nine places for the decimal component.

# Numerical variables.
NUM1="$1"
NUM2="$3"
# Equalise the decimal places.
FP1=$( printf "%.9f" "$NUM1" )
FP2=$( printf "%.9f" "$NUM2" )

# Calculation mode variable, limited error correction.
CALC="$2"
if [ "$NUM1" = "" ] || [ "$NUM2" = "" ] || [ "$CALC" = "" ]
then
	echo "USAGE: calc NUMBER1 +|-|*|/ NUMBER2"
	exit 1
fi

# Remove the decimal point(s).
INT1="${FP1%.*}${FP1#*.}"
INT2="${FP2%.*}${FP2#*.}"
# Remove all leading zeros.
INT1=$( printf "%.f" "$INT1" )
INT2=$( printf "%.f" "$INT2" )

# Addition and subtraction.
if [ "$CALC" = "+" ] || [ "$CALC" = "-" ]
then
	if [ "$CALC" = "+" ]
	then
		SUM=$(( (INT1)+(INT2) ))
	fi
	if [ "$CALC" = "-" ]
	then
		SUM=$(( (INT1)-(INT2) ))
	fi
	# Internal Variables.
	LENGTH="${#SUM}"
	MINUS="$SUM"
	FLOAT=""
	COUNT="0"

	# Detect the minus sign, save and delete.
	while [ "$COUNT" -le "$LENGTH" ]
	do
		MINUS="${MINUS%?}"
		if [ "$MINUS" = "-" ]
		then
			SUM="${SUM#?}"
			MINUS="-"
			break
		fi
		COUNT=$(( COUNT+1 ))
	done

	# Give a positive sign on the final result if it is not negative.
	# This condition can be removed if required.
	if [ "$MINUS" = "" ]
	then
		MINUS="+"
	fi

	# Pad a float result of less that 1.0 with leading zeros if required.
	LENGTH=${#SUM}
	if [ "$LENGTH" -le "9" ]
	then
		FLOAT=$( printf "%09d" "$SUM" )
		SUM="$FLOAT"
	fi

	# Obtain the integer part.
	INT="${SUM%?????????}"
	if [ "$INT" = "" ]
	then
		INT="0"
	fi

	# Obtain the float part.
	if [ "$LENGTH" -ge "10" ]
	then
		COUNT="10"
		GETFLOAT=""
		while [ "$COUNT" -le "$LENGTH" ]
		do
			GETFLOAT="$GETFLOAT"'?'
			COUNT=$(( COUNT+1 ))
		done
		FLOAT="${SUM#${GETFLOAT}}"
	fi
	RESULT="${INT}.${FLOAT}"
	printf "%s%.9f\n" "$MINUS" "$RESULT"
fi

# Multiplication.
if [ "$CALC" = "*" ]
then
	:
fi

# Division.
if [ "$CALC" = "/" ]
then
	:
fi

A couple of 'additions' using python 2.7 as a check.
OSX 10.13.6, default bash terminal calling /usr/local/bin/dash
Code:
Last login: Mon Nov  5 08:06:19 on ttys000
AMIGA:amiga~> cd Desktop/Code/Shell
AMIGA:amiga~/Desktop/Code/Shell> ./calc 1234567.987654321 - -1234567.135790864
+2469135.123445185
AMIGA:amiga~/Desktop/Code/Shell> python
Python 2.7.10 (default, Oct  6 2017, 22:29:07) 
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 1234567.987654321 - -1234567.135790864
2469135.123445185
>>> exit()
AMIGA:amiga~/Desktop/Code/Shell> ./calc 0.00001234567890 - 1.012401299
-1.012388953
AMIGA:amiga~/Desktop/Code/Shell> python
Python 2.7.10 (default, Oct  6 2017, 22:29:07) 
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 0.00001234567890 - 1.012401299
-1.0123889533211
>>> exit()
AMIGA:amiga~/Desktop/Code/Shell> _

Have fun and enjoy...
This is my pet love, getting languages to do something they were not designed to do.

Bazza.
These 2 Users Gave Thanks to wisecracker For This Post:
# 2  
OSX 10.13.6, default bash terminal.
NOTE: These shells are of a vintage that Apple thinks is OK!

Well guys, have I found a bug?
Code:
Last login: Tue Nov  6 13:15:41 on ttys000
AMIGA:amiga~> cd Desktop/Code/Shell
AMIGA:amiga~/Desktop/Code/Shell> # 'calc' on diferent shells.
AMIGA:amiga~/Desktop/Code/Shell> # shebang set to 'ksh' in the script.
AMIGA:amiga~/Desktop/Code/Shell> ./calc 101010101.909090909 - -123456789.987654321
+224466891.896745230
AMIGA:amiga~/Desktop/Code/Shell> # shebang set to 'sh' in the script.
AMIGA:amiga~/Desktop/Code/Shell> ./calc 101010101.909090909 - -123456789.987654321
+224466891.896745235
AMIGA:amiga~/Desktop/Code/Shell> # shebang set to 'bash' in the script.
AMIGA:amiga~/Desktop/Code/Shell> ./calc 101010101.909090909 - -123456789.987654321
+224466891.896745235
AMIGA:amiga~/Desktop/Code/Shell> # shebang set to 'dash' in the script.
AMIGA:amiga~/Desktop/Code/Shell> ./calc 101010101.909090909 - -123456789.987654321
+224466891.896745235
AMIGA:amiga~/Desktop/Code/Shell> # shebang set to 'zsh' in the script.
AMIGA:amiga~/Desktop/Code/Shell> ./calc 101010101.909090909 - -123456789.987654321
+224466891.224466890
AMIGA:amiga~/Desktop/Code/Shell> # OH DEAR, WHAT WENT WRONG!
AMIGA:amiga~/Desktop/Code/Shell> python
Python 2.7.10 (default, Oct  6 2017, 22:29:07) 
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 101010101.909090909 - -123456789.987654321
224466891.89674523
>>> exit()
AMIGA:amiga~/Desktop/Code/Shell> zsh --version
zsh 5.3 (x86_64-apple-darwin17.0)
AMIGA:amiga~/Desktop/Code/Shell> _

'zsh' version, zsh 5.3 (x86_64-apple-darwin17.0)
It might be that whatever is in the script that is causing the 'zsh' error has been cured, so could someone with a current version check please.

Bazza.
# 3  
Your use of printf "%f" is perhaps begging for trouble, that may convert to floating point and back. And even 64-bit floating point doesn't have the exactness of a 64-bit integer, some of those 64 bits get dedicated to mantissa et cetera.

dash gives +224466891.896745235 on my system, bash and ksh give +224466891.896745230, and zsh doesn't work at all, it gives "invalid floating point number" on printf "%s%.9f\n" "$MINUS" "$RESULT"

Last edited by Corona688; 11-06-2018 at 11:31 AM..
# 4  
My suggestion would be to eliminate these floating point conversions, and just use integer math. For three decimal points you'd do

Code:
SIGN="+"
if [ "$NUM" -lt 0 ]
then
        NUM=$((-NUM))
        SIGN="-"
fi

printf "%s%d.%d" "$SIGN" "$((NUM/100))" "$((NUM%100))"

# 5  
Quote:
Originally Posted by Corona688
Your use of printf "%f" is perhaps begging for trouble, that may convert to floating point and back. And even 64-bit floating point doesn't have the exactness of a 64-bit integer, some of those 64 bits get dedicated to mantissa et cetera.

dash gives +224466891.896745235 on my system, bash and ksh give +224466891.896745230, and zsh doesn't work at all, it gives "invalid floating point number" on printf "%s%.9f\n" "$MINUS" "$RESULT"
I wouldn't know where to start for floating point maths in pure 'dash' and I'll stick my neck out and say it cannot be done at all.

I don't use "%f" anywhere, I use "%.f" instead and 'shellcheck' shows no issues using 'dash' as the shell and changing it to 'sh' too.

I did quote that up 1,000,000.000000000, (1 Million), it is accurate to 9 decimal places and gets progressively worse to the upper/lower limits. Therefore IMO 8 places accuracy to at least 22 Million is acceptable for ordinary work

As for the code snippets I will try them out whilst working out multiply and divide; and I thought 'add' and 'subtract' were hard.
Doing it on the command line is not the same as working out the algorithms for limited language like 'dash'. All those 'bash'isms one takes for granted.

I have no idea why 'zsh' fails when the others work as per POSIX, I don't really intend to find out as dash is the important shell ATM...
(I have not got 'ash' so dunno if it works on that.)

EDIT:
Done manually...
Code:
Last login: Tue Nov  6 19:05:47 on ttys000
AMIGA:amiga~> cd Desktop/Code/Shell
AMIGA:amiga~/Desktop/Code/Shell> ./calc .000000277 - +.000000092
+0.000000185
AMIGA:amiga~/Desktop/Code/Shell> # Therefore SUM="000000185"
AMIGA:amiga~/Desktop/Code/Shell> FLOAT="000000185"
AMIGA:amiga~/Desktop/Code/Shell> INT=""
AMIGA:amiga~/Desktop/Code/Shell> NUMBER=$(( FLOAT/100 ))
-bash: 000000185: value too great for base (error token is "000000185")
AMIGA:amiga~/Desktop/Code/Shell> dash
AMIGA:\u\w> ./calc .000000277 - +.000000092
+0.000000185
AMIGA:\u\w> # Therefore SUM="000000185"
AMIGA:\u\w> FLOAT="000000185"
AMIGA:\u\w> INT=""
AMIGA:\u\w> NUMBER=$(( FLOAT/100 ))
dash: 6: Illegal number: 000000185
AMIGA:\u\w> exit
AMIGA:amiga~/Desktop/Code/Shell> _

Bazza...

Last edited by wisecracker; 11-06-2018 at 03:37 PM.. Reason: Manual reason for not using 'x%100'.
# 6  
Quote:
Originally Posted by wisecracker
I wouldn't know where to start for floating point maths in pure 'dash' and I'll stick my neck out and say it cannot be done at all.
I misread your post as dash misbehaving rather than zsh, but anyway - I don't mean the shell has floating point. I mean shell printf could use it as a temporary intermediate to implement %f.
Quote:
I don't use "%f" anywhere, I use "%.f"
potato, potahto. The simplest way to implement %f is the C function calls strtof and sprintf, which would use a floating point intermediate. It'd unprocess the number into a float then immediately process it back into a string using the "%.f" specifier. Less than a line.

Quote:
Done manually...
Code:
Last login: Tue Nov  6 19:05:47 on ttys000
AMIGA:amiga~> cd Desktop/Code/Shell
AMIGA:amiga~/Desktop/Code/Shell> ./calc .000000277 - +.000000092
+0.000000185
AMIGA:amiga~/Desktop/Code/Shell> # Therefore SUM="000000185"
AMIGA:amiga~/Desktop/Code/Shell> FLOAT="000000185"
AMIGA:amiga~/Desktop/Code/Shell> INT=""
AMIGA:amiga~/Desktop/Code/Shell> NUMBER=$(( FLOAT/100 ))
-bash: 000000185: value too great for base (error token is "000000185")
AMIGA:amiga~/Desktop/Code/Shell> dash
AMIGA:\u\w> ./calc .000000277 - +.000000092
+0.000000185
AMIGA:\u\w> # Therefore SUM="000000185"
AMIGA:\u\w> FLOAT="000000185"
AMIGA:\u\w> INT=""
AMIGA:\u\w> NUMBER=$(( FLOAT/100 ))
dash: 6: Illegal number: 000000185
AMIGA:\u\w> exit
AMIGA:amiga~/Desktop/Code/Shell> _

Bazza...
You're taking the path of maximum resistance again. If you leave out the decimal point entirely and assume all numbers are multiplied by 100, addition and subtraction "just work", no zero padding required. Then you use division and modulus to extract the full and fractional parts later.

Last edited by Corona688; 11-06-2018 at 04:15 PM..
# 7  
Here's a quick bash-only version for positive numbers. The trick is reading the values in. Once you do that the rest is straightforward.

Code:
DP=3
MOD=1000

function readnum {
        IFS="." read N FRAC <<<"$1"
        [ "$N" = "0" ] && N=""
        for((X=0; X<DP; X++))
        do
                D="${FRAC:$X:1}"
                [ -z "$D" ] && D="0"
                N="$N$D" # Prepend digits to N
        done
}

readnum "0.5"

echo "Integer $N is fixed point $((N/MOD)).$((N%MOD))"

This User Gave Thanks to Corona688 For This Post:
Login or Register for Dates, Times and to Reply

Previous Thread | Next Thread
Thread Tools Search this Thread
Search this Thread:
Advanced Search

7 More Discussions You Might Find Interesting

1. UNIX for Beginners Questions & Answers

How to create a new mount point with 600GB and add 350 GBexisting mount point? IN AIX

How to create a new mount point with 600GB and add 350 GBexisting mount point Best if there step that i can follow or execute before i mount or add diskspace IN AIX Thanks (2 Replies)
Discussion started by: Thilagarajan
2 Replies

2. Shell Programming and Scripting

Fixed mount point for a USB cardreader (Raspberry Pi, UDEV)

Hey all! :) I'm trying to create a fixed mount point for an usb cardreader. I've found a script on a raspberry pi forum which does the following: usb stick is plugged in -> script checks the mount point for data -> script starts copying the files automatically -> script unmounts the... (0 Replies)
Discussion started by: Eomer
0 Replies

3. Post Here to Contact Site Administrators and Moderators

How to sum up data in fixed width file with decimal point?

HI Everyone, I have below source file AAA|NAME1|ADDRESS1|300.20 BBB|NAME2|ADDRESS2|400.31 CCC|NAME3|ADDRESS3|300.34 I have requirement where I need to sum up fourth field in above fixed width pipe delimited flat file. When I use below code, it gives me value 1001.00 But I am expecting... (1 Reply)
Discussion started by: patricjemmy6
1 Replies

4. Shell Programming and Scripting

How to perform a hexdump using dd from start point to end point?

hi, I would like to ask or is it possible to dump a hex using dd from starting point to end point just like the "xxd -s 512 -l 512 <bin file>" I know the redirect hexdump -C but i can't figure it out the combination options of dd. Hope someone can share their knowledge.. Thanks in... (3 Replies)
Discussion started by: jao_madn
3 Replies

5. Shell Programming and Scripting

remove dash

hi I am using ksh #A="abc-def" #typeset -u B="$A" #echo $B ABC-DEF how to remove the dash? i.e. ABCDEF? thank you (1 Reply)
Discussion started by: melanie_pfefer
1 Replies

6. UNIX for Dummies Questions & Answers

dash after ampersant

Hi! I'm new in these forums and more or less new with Unix. So... here is the question: does anyone know where is redirected the output of a command when you put >&- after it? Does it means any standard file descriptor? Thanks! (2 Replies)
Discussion started by: csecnarf
2 Replies

7. UNIX for Advanced & Expert Users

Fibre connection Point to Point SUN

Anyone know of a guide or instructions for Solaris I got to configure a SBUS HBA to talk to a tape robot. I have done this on a switch but not point to point. just going HBA >>>>> TAPE Fibre simple two nodes Kie (6 Replies)
Discussion started by: kie
6 Replies

Featured Tech Videos