Ceil and Floor Link to this heading

Backstory Link to this heading

Some time ago, I was thinking about coding experiments that would be easy to do, expand on, and also interesting. One popped into my head then, and so now I’m doing it. Basically, not knowing exactly what magic built-ins do in various languages to be fast and accurate has always interested me, and since I’ve been doing some coding prep I’ve been using Python a lot, ans specifically I find myself using ceil and floor a lot.

So I thought to myself, why not try to reproduce whatever is happening behind the scenes? I’ll be doing so without any research or looking at the source code, of course. I’m just going to try and figure it out by intuition and past experience.

Intro/setup code Link to this heading

python
1# base imports
2from timeit import default_timer as timer
3import math
4from matplotlib import pyplot as plt
5import numpy as np
python
 1# define some test cases
 2test_cases = [2, 2.0, 2.1, 2.4, 2.5, 2.55, 2.7, 3, 3.0]
 3test_case_ceil_answers = [2, 2, 3, 3, 3, 3, 3, 3, 3]
 4test_case_floor_answers = [2, 2, 2, 2, 2, 2, 2, 3, 3]
 5
 6# test just to test the expediency of running a function once
 7def run_single_test(test_func):
 8    start = timer()
 9    test_func(test_cases[0])
10    end = timer()
11    return end - start
12
13# test runner function
14def run_tests_cases(test_func, answers):
15    start = timer()
16    for i, t in enumerate(test_cases):
17        ans = test_func(test_cases[i])
18        if ans != answers[i]:
19            raise Exception(f"Incorrect answer. Test case {i + 1}: (ans: {ans}, actual: {answers[i]})")
20    end = timer()
21    return end - start

Baseline Link to this heading

Let's first compare the time it takes to run the native ceil and floor functions. These will be our baseline.

python
1# baseline tests
2ceil_base_time = run_single_test(ceil)
3floor_base_time = run_single_test(floor)
4ceil_tests_time = run_tests_cases(ceil, test_case_ceil_answers)
5floor_tests_time = run_tests_cases(floor, test_case_floor_answers)
6
7ceil_base_time, floor_base_time, ceil_tests_time, floor_tests_time
(2.916996891144663e-06,
 1.2089949450455606e-06,
 6.707996362820268e-06,
 2.4170003598555923e-06)

First experiment: int Link to this heading

My first thought for floor is just to run int(). Let's see how that compares.

python
1int_func = lambda x: int(x)
2int_time = run_single_test(int_func)
3int_tests_time = run_tests_cases(int_func, test_case_floor_answers)
4
5int_time, int_tests_time
(1.2079981388524175e-06, 4.6670029405504465e-06)
python
1%matplotlib inline
2x=np.array(["floor single", "int single", "floor tests", "int tests"])
3y=np.array([floor_base_time, int_time, floor_tests_time, int_tests_time])
4fig=plt.bar(x,y)
5plt.title("Test Runtime")
6plt.ylabel("Time")

as good as floor? maybe not.

On a single run's basis, int can be as fast or even a bit faster than floor, but if used many times in a row it appears it doesn't hold up to floor at all, and can be at least twice as slow in this small test case.

More coming soon…