WiPyFloat.py

Questions and discussion about The WiPy 1.0 board and CC3200 boards.
Target audience: Users with a WiPy 1.0 or CC3200 board.
Post Reply
jgmdavies
Posts: 57
Joined: Tue Aug 09, 2016 2:39 pm

WiPyFloat.py

Post by jgmdavies » Tue Aug 30, 2016 8:33 am

Herewith an attempt at a standalone float package for the WiPy, together with some tests.

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

Float_tests.py

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

Post Reply