I'll post further code updates to this topic.
Jim
WiPyFloat.py
Code: Select all
# WiPyFloat.py
# Last modified: 27 Aug 2016
# J.G.Davies, Jacobus Systems Ltd., Brighton & Hove, UK.
# This code is hereby released as Public Domain. I would appreciate an acknowledgement if you find it useful.
class Float():
mantissa = 0
exponent = 0
def __init__(self, f=None):
if f:
if (type(f).__name__ == "Float"):
self.mantissa = f.mantissa
self.exponent = f.exponent
elif (type(f).__name__ == "int"):
self.mantissa = int(f)
self.exponent = 0
elif (type(f).__name__ == "str"):
self.load(str(f))
self.normalise()
return
# Operator overloads.
def __abs__(self):
result = Float(self)
result.mantissa = abs(result.mantissa)
return result
def __add__(self, other):
return Float.add(self, other)
def __eq__(self, other):
self.normalise()
other.normalise()
return (self.mantissa == other.mantissa) and (self.exponent == other.exponent)
def __mul__(self, other):
return Float.multiply(self, other)
def __neg__(self):
result = Float(self)
result.mantissa = -result.mantissa
return result
def __pos__(self):
return Float(self)
def __str__(self):
return self.str()
def __sub__(self, other):
return Float.subtract(self, other)
def __truediv__(self, other):
return Float.divide(self, other)
# Static methods.
@staticmethod
def add(f1, f2):
# Return a new Float holding (f1 + f2).
result = Float(f1)
result.add_float(f2)
return result
@staticmethod
def divide(f1, f2):
# Return a new Float holding (f1 / f2).
result = Float(f1)
result.divide_float(f2)
return result
@staticmethod
def multiply(f1, f2):
# Return a new Float holding (f1 * f2).
result = Float(f1)
result.multiply_float(f2)
return result
@staticmethod
def subtract(f1, f2):
# Return a new Float holding (f1 - f2).
result = Float(f1)
result.subtract_float(f2)
return result
# Methods.
def add_float(self, f, add=True):
# Add Float 'f' to this Float.
# Align the exponents.
self_mantissa = self.mantissa
self_exponent = self.exponent
f_mantissa = f.mantissa
f_exponent = f.exponent
if (f_exponent < self_exponent):
shift = self_exponent - f_exponent
self_mantissa *= 10 ** shift
self_exponent -= shift
elif (f_exponent > self_exponent):
shift = f_exponent - self_exponent
f_mantissa *= 10 ** shift
f_exponent -= shift
if add:
self.mantissa = self_mantissa + f_mantissa
else:
self.mantissa = self_mantissa - f_mantissa
self.exponent = self_exponent
self.normalise()
return self
def debug(self):
s = "Float: {0}, {1}".format(self.mantissa, self.exponent)
return s
def divide_float(self, f):
# Divide this Float by 'f'.
self.mantissa *= 100000000
self.exponent -= 8
self.mantissa //= f.mantissa
self.exponent -= f.exponent
self.normalise()
return self
def int(self):
result = 0
return result
def load(self, s):
s = s.strip().lower()
pos = True
if (s[0] == "+"):
s = s[1:]
elif (s[0] == "-"):
s = s[1:]
pos = False
if "e" in s:
return self.load_scientific(s, pos)
if "." in s:
whole, decimal = s.split(".")
if not decimal:
# There is no decimal part.
# TODO: if 'whole' ends in zeros, could scale the number and set self.exponent.
# This would help keep 'mantissa' a simple 31-bit int on the WiPy.
self.mantissa = int(whole)
if not pos: self.mantissa = -self.mantissa
self.exponent = 0
self.normalise()
return True
# There is a decimal part.
if whole and (int(whole) == 0):
whole = None
if not whole:
# There is no whole part.
value, nleading = self.parse_decimal(decimal)
if value:
self.mantissa = int(value)
if not pos: self.mantissa = -self.mantissa
self.exponent = -nleading - len(value)
self.normalise()
else:
self.mantissa = 0
self.exponent = 0
return True
# There is a whole part and a decimal part.
whole = whole.lstrip("0")
decimal = decimal.rstrip('0')
self.mantissa = int(whole + decimal)
if not pos: self.mantissa = -self.mantissa
self.exponent = -len(str(decimal))
self.normalise()
else:
# TODO: if 'whole' ends in zeros, could scale the number and set self.exponent.
# This would help keep 'mantissa' a simple 31-bit int on the WiPy.
self.mantissa = int(s)
if not pos: self.mantissa = -self.mantissa
self.exponent = 0
self.normalise()
return True
def multiply_float(self, f):
# Multiply this Float by 'f'.
self.mantissa *= f.mantissa
self.exponent += f.exponent
self.normalise()
return self
def normalise(self):
# Normalise where possible.
if (self.mantissa != 0):
s = str(self.mantissa)
stripped = s.rstrip("0")
diff = len(s) - len(stripped)
if (diff > 0):
self.mantissa = int(stripped)
self.exponent += diff
return True
def str(self, format="F"):
if ("E" in format):
return self.str_scientific()
# Use format "F".
pos = (self.mantissa >= 0)
result = str(abs(self.mantissa))
if (self.exponent > 0):
result += "0" * self.exponent
elif (self.exponent < 0):
if (-self.exponent < len(result)):
cut = len(result) + self.exponent
result = result[:cut] + "." + result[cut:]
else:
n_zeros = -self.exponent - len(result)
result = "0." + ("0" * n_zeros) + result
if not pos: result = "-" + result
return result
def subtract_float(self, f):
# Subtract Float 'f' from this Float.
return self.add_float(f, False)
# Helper methods.
def load_scientific(self, s, pos):
mant, exp = s.split("e")
self.load(mant)
self.exponent += int(exp)
self.normalise()
return False
def parse_decimal(self, s):
nleading = 0
for c in s:
if (c != "0"): break
nleading += 1
if (nleading == 0):
value = s
else:
value = s[nleading:]
return value, nleading
def str_scientific(self):
pos = (self.mantissa >= 0)
result = str(abs(self.mantissa))
len_mantissa = len(result)
if (len(result) > 1):
result = result[:1] + "." + result[1:]
result = result.rstrip("0")
result = result.rstrip(".")
result += "e" + str(self.exponent + len_mantissa - 1)
if not pos: result = "-" + result
return result
if (__name__ == "__main__" ):
import Float_tests
Code: Select all
# Float_tests.py
# Last modified: 30 Aug 2016
# J.G.Davies, Jacobus Systems Ltd., Brighton & Hove, UK.
# This code is hereby released as Public Domain. I would appreciate an acknowledgement if you find it useful.
from WiPyFloat import Float
npass = 0
nfail = 0
def test1():
# 'load' and 'str' methods.
print("test1 - load / str")
f1 = Float()
f1.load("0")
print(PassFail(f1.str() == "0"), "0 ->", f1, f1.str("E"))
f1.load("0.00")
print(PassFail(f1.str() == "0"), "0.00 ->", f1, f1.str("E"))
f1.load(".0000")
print(PassFail(f1.str() == "0"), ".0000 ->", f1, f1.str("E"))
f1.load("1.3")
print(PassFail(f1.str() == "1.3"), "1.3 ->", f1, f1.str("E"))
f1.load("-1.3")
print(PassFail(f1.str() == "-1.3"), "-1.3 ->", f1, f1.str("E"))
f1.load("2000.05")
print(PassFail(f1.str() == "2000.05"), "2000.05 ->", f1, f1.str("E"))
f1.load("0.01")
print(PassFail(f1.str() == "0.01"), "0.01 ->", f1, f1.str("E"))
f1.load("0.0123")
print(PassFail(f1.str() == "0.0123"), "0.0123 ->", f1, f1.str("E"))
f1.load("0.00123")
print(PassFail(f1.str() == "0.00123"), "0.00123 ->", f1, f1.str("E"))
f1.load("1.23e-3")
print(PassFail(f1.str() == "0.00123"), "1.23e-3 ->", f1, f1.str("E"))
f1.load("1.23e6")
print(PassFail(f1.str() == "1230000"), "1.23e6 ->", f1, f1.str("E"))
f1.load("5e6")
print(PassFail(f1.str() == "5000000"), "5e6 ->", f1, f1.str("E"))
print("")
return True
def test2():
# Addition.
print("test2 - Addition")
f1 = Float()
f2 = Float()
f1.load("2000.05")
f2.load("0.001")
f3 = Float.add(f1, f2)
print(PassFail(f3.str() == "2000.051"), f1, "+", f2, "->", f3, f3.str("E"))
f1.load("0.1")
f2.load("0.2")
f3 = Float.add(f1, f2)
print(PassFail(f3.str() == "0.3"), f1, "+", f2, "->", f3, f3.str("E"))
f1.load("500")
f2.load("600")
f3 = Float.add(f1, f2)
print(PassFail(f3.str() == "1100"), f1, "+", f2, "->", f3, f3.str("E"))
f1.load("500")
f2.load("-600")
f3 = Float.add(f1, f2)
print(PassFail(f3.str() == "-100"), f1, "+", f2, "->", f3, f3.str("E"))
f1.load("-500")
f2.load("600")
f3 = Float.add(f1, f2)
print(PassFail(f3.str() == "100"), f1, "+", f2, "->", f3, f3.str("E"))
print("")
return True
def test3():
# Subtraction.
print("test3 - Subtraction")
f1 = Float()
f2 = Float()
f1.load("2000.05")
f2.load("0.001")
f3 = Float.subtract(f1, f2)
print(PassFail(f3.str() == "2000.049"), f1, "-", f2, "->", f3, f3.str("E"))
f1.load("0.1")
f2.load("0.2")
f3 = Float.subtract(f1, f2)
print(PassFail(f3.str() == "-0.1"), f1, "-", f2, "->", f3, f3.str("E"))
f1.load("10")
f2.load("0.6667")
f3 = Float.subtract(f1, f2)
print(PassFail(f3.str() == "9.3333"), f1, "-", f2, "->", f3, f3.str("E"))
f1.load("500")
f2.load("600")
f3 = Float.subtract(f1, f2)
print(PassFail(f3.str() == "-100"), f1, "-", f2, "->", f3, f3.str("E"))
f1.load("500")
f2.load("-600")
f3 = Float.subtract(f1, f2)
print(PassFail(f3.str() == "1100"), f1, "-", f2, "->", f3, f3.str("E"))
f1.load("-500")
f2.load("600")
f3 = Float.subtract(f1, f2)
print(PassFail(f3.str() == "-1100"), f1, "-", f2, "->", f3, f3.str("E"))
print("")
return True
def test4():
# Multiplication.
print("test4 - Multiplication")
f1 = Float()
f2 = Float()
f1.load("5")
f2.load("6")
f3 = Float.multiply(f1, f2)
print(PassFail(f3.str() == "30"), f1, "*", f2, "->", f3, f3.str("E"))
f1.load("500")
f2.load("600")
f3 = Float.multiply(f1, f2)
print(PassFail(f3.str() == "300000"), f1, "*", f2, "->", f3, f3.str("E"))
f1.load("5e3")
f2.load("6e3")
f3 = Float.multiply(f1, f2)
print(PassFail(f3.str() == "30000000"), f1, "*", f2, "->", f3, f3.str("E"))
f1.load("5e6")
f2.load("6e6")
f3 = Float.multiply(f1, f2)
print(PassFail(f3.str() == "30000000000000"), f1, "*", f2, "->", f3, f3.str("E"))
print("")
return True
def test5():
# Division.
print("test5 - Division")
f1 = Float()
f2 = Float()
f1.load("1")
f2.load("3")
f3 = Float.divide(f1, f2)
print(PassFail(f3.str()[:7] == "0.33333"), f1, "/", f2, "->", f3, f3.str("E"), f3.debug())
f1.load("5")
f2.load("6")
f3 = Float.divide(f1, f2)
print(PassFail(f3.str()[:7] == "0.83333"), f1, "/", f2, "->", f3, f3.str("E"), f3.debug())
f1.load("500")
f2.load("600")
f3 = Float.divide(f1, f2)
print(PassFail(f3.str()[:7] == "0.83333"), f1, "/", f2, "->", f3, f3.str("E"), f3.debug())
f1.load("5e3")
f2.load("6e3")
f3 = Float.divide(f1, f2)
print(PassFail(f3.str()[:7] == "0.83333"), f1, "/", f2, "->", f3, f3.str("E"), f3.debug())
f1.load("5e6")
f2.load("6e6")
f3 = Float.divide(f1, f2)
print(PassFail(f3.str()[:7] == "0.83333"), f1, "/", f2, "->", f3, f3.str("E"), f3.debug())
print("")
return True
def test6():
# Object creation options.
print("test6 - Object creation")
f1 = Float()
print(PassFail(f1.str() == "0"), f1, f1.debug())
f1 = Float(123)
print(PassFail(f1.str() == "123"), f1, f1.debug())
f1 = Float("3.1416")
print(PassFail(f1.str() == "3.1416"), f1, f1.debug())
f2 = Float(f1)
print(PassFail(f1.str() == "3.1416"), f2, f2.debug())
print("")
return True
def test7():
# Equality / Inequality operator overloads.
print("test7 - Equality / Inequality operator overloads")
f1 = Float(123)
f2 = Float("123")
f3 = Float("123.0001")
print(PassFail(f1 == f2), "(", f1, "==", f2, ") is", f1 == f2)
print(PassFail(f1 != f3), "(", f1, "==", f3, ") is", f1 == f3)
print(PassFail(f1 == f2), "(", f1, "!=", f2, ") is", f1 != f2)
print(PassFail(f1 != f3), "(", f1, "!=", f3, ") is", f1 != f3)
print("")
return True
def test8():
# test8 - Arithmetic operator overloads.
print("test8 - Arithmetic operator overloads")
f1 = Float("1.8")
f2 = Float("3.7")
f3 = f1 + f2
print(PassFail(f3.str() == "5.5"), f1, "+", f2, "->", f3, f3.str("E"))
f3 = f1 - f2
print(PassFail(f3.str() == "-1.9"), f1, "-", f2, "->", f3, f3.str("E"))
f3 = f1 * f2
print(PassFail(f3.str() == "6.66"), f1, "*", f2, "->", f3, f3.str("E"))
f3 = f1 / f2
print(PassFail(f3.str()[:8] == "0.486486"), f1, "/", f2, "->", f3, f3.str("E"))
return True
def PassFail(cond):
global npass
global nfail
if cond:
npass += 1
return "PASS"
nfail += 1
return "FAIL"
test1()
test2()
test3()
test4()
test5()
test6()
test7()
test8()
print(npass, "passed,", nfail, "failed")