Gabriel-p
5/7/2016 - 7:00 PM

error_round_test.py

import numpy as np
from random import randint
from decimal import Decimal
from math import log10, floor


def floats_5(N):
    """
    Generate random floats between a random range, where all floats
    end with a '5'.
    """
    # rang = randint(1, 1)
    flts = np.random.uniform(0., 1., N)

    # Add '5' to the end of each random float, with different lengths.
    fl_5 = []
    for f in flts:
        # Trim float.
        i = randint(2, len(str(f).split('.')[1]))
        # Create trimmed float that ends with a '5' .
        f = Decimal(str(f).split('.')[0] + '.' + str(f).split('.')[1][:i] +
                    '5')
        fl_5.append(f)

    return fl_5


def format_e(x):
    """
    Taken from: http://stackoverflow.com/a/6913576/1391441
    """
    a = '%E' % x
    return a.split('E')[0].rstrip('0').rstrip('.') + 'E' + a.split('E')[1]


def format_5(f, n):
    """
    Round floats that end with a '5' correctly, bypassing Python's issue
    with the round() function.

    See: http://codereview.stackexchange.com/q/122243/35351
    """
    # If the last digit is 5 or larger, round up.
    if int(f[-1]) >= 5:
        r_up_down = 'ROUND_UP'
    else:
        r_up_down = 'ROUND_DOWN'

    return Decimal.quantize(Decimal(f), Decimal(f[:-n]), rounding=r_up_down)


def round_to_n(x, n=1):
    """
    Round float to n significant figures.

    >>> round_to_n(31.51)
    30.0
    >>> round_to_n(1.532)
    2.0
    >>> round_to_n(2156.23)
    2000.0
    >>> round_to_n(156.53, 3)
    157.0
    >>> round_to_n(0.00235, 4)
    0.00235
    >>> round_to_n(0.66, 1)
    0.7
    >>> round_to_n(0.075)
    0.08
    >>> round_to_n(0.075, 2)
    0.075
    """

    if x == 0.:
        x_r = 0.
    elif 0. < abs(x) < 1.:
        # Count number of decimal places.
        nx = len(str(x).split('.')[1])
        print 'n, nx:', n, nx
        if n >= nx:
            # Keep all decimals.
            x_r = x
        elif n < nx:
            # Decimal part of the float.
            f1 = str(x).split('.')[1]
            # Number of significant digits in float.
            nd = len(f1.lstrip('0'))
            # Number of leading zeroes after the decimal dot.
            n0 = len(f1) - nd
            print 'n0, nd:', n0, nd
            if n < nd:
                # New float with no leading zeroes.
                x1 = '0.' + f1.lstrip('0')[:n+1]
                print 'x1:', x1
                # Round float.
                x2 = format_5(x1, nd-(n+1))
                print 'x2:', x2
                if n0 > 0:
                    # Reconstruct float.
                    x_r = float('0.' + '0'*n0 + str(x2).split('.')[1])
                else:
                    x_r = float('0.' + str(x2).split('.')[1])
            else:
                x_r = x
    else:
        pass

    return x_r


for f in [0., 0.01354, 0.00015, 0.25, 0.0075]:  # floats_5(10)
    print 'float:', f
    new_f = round_to_n(f, 1)
    print 'new_f:', new_f, '\n'