Skip to content

Instantly share code, notes, and snippets.

@PurpleMyst
Last active July 27, 2017 23:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PurpleMyst/28dd0c1e84df88ff8854c2775ddff0ac to your computer and use it in GitHub Desktop.
Save PurpleMyst/28dd0c1e84df88ff8854c2775ddff0ac to your computer and use it in GitHub Desktop.
Fast way of calculating the number of digits in a number in Python, using C
#!/usr/bin/env python3
from math import floor, log10
from digits import bacherikow, intlen, bordum, stephen, bisect
def python_strlen(n, str=str, len=len):
return len(str(n))
def python_intlen(n, floor=floor, log10=log10):
return floor(log10(n)) + 1
def python_bacherikow(n, enumerate=enumerate,
table=[10**x for x in range(1, 10)]):
for i, k in enumerate(table):
if n < k:
return i + 1
def python_bordum(n):
l = 1
base = k = 10
while n >= k:
k *= base
l += 1
return l
def main():
import random
import sys
from timeit import timeit
n = random.randint(10 ** 8, 10 ** 9)
tests = 1000000
print(f"Running under {sys.implementation.name}")
for func in (bacherikow, intlen, bordum, stephen, bisect,
python_bacherikow, python_intlen, python_bordum,
python_strlen):
assert func(132890) == 6, func.__name__
thyme = timeit(f"{func.__name__}({n!r})",
setup=f"from __main__ import {func.__name__}",
number=tests)
print(f"{func.__module__}.{func.__name__}({n!r}) ran {tests} times "
f"takes {thyme * 1000:.2f} milliseconds")
if __name__ == "__main__":
main()
#include <Python.h>
#include <math.h>
// Compilation command:
// clang digits.c -o digits.so -Ofast -std=c99 -shared -fPIC -I /usr/include/python3.6
static PyObject*
digits_intlen(PyObject *self, PyObject *args) {
int num;
if (!PyArg_ParseTuple(args, "i", &num)) return NULL;
return PyLong_FromLong(1 + floor(log10(num)));
}
// TODO: Build this list with a macro.
static const int powers[] = {10, 100, 1000, 10000, 100000, 1000000, 10000000}; // and so on...
static const int power_amount = sizeof powers / sizeof powers[0];
static PyObject*
digits_bacherikow(PyObject *self, PyObject *args) {
int num;
if (!PyArg_ParseTuple(args, "i", &num)) return NULL;
for (int i = 1; i <= power_amount; ++i) {
if (num - powers[i - 1] < 0) return PyLong_FromLong(i);
}
Py_RETURN_NONE;
}
static int bisect_log10(long int n, int s, int e) {
int a = (e - s) / 2 + s;
if(s >= e)
return s;
if((powers[a] - n) <= 0)
return bisect_log10(n, a + 1, e);
else
return bisect_log10(n, s, a);
}
static PyObject*
digits_bisect(PyObject *self, PyObject *args) {
int num;
if (!PyArg_ParseTuple(args, "i", &num)) return NULL;
return PyLong_FromLong(1 + bisect_log10(num, 0, power_amount));
}
static PyObject*
digits_bordum(PyObject *self, PyObject *args) {
int num;
if (!PyArg_ParseTuple(args, "i", &num)) return NULL;
int l = 1;
int k = 10;
int base = 10;
// TODO: Maybe make this a for loop?
while (num >= k) {
k *= base;
l += 1;
}
return PyLong_FromLong(l);
}
static unsigned int baseTwoDigits(unsigned int x) {
return x ? 32 - __builtin_clz(x) : 0;
}
static unsigned int tenToThe[] = {
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
};
static unsigned int guess[33] = {
0, 0, 0, 0, 1, 1, 1, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 5, 5, 5,
6, 6, 6, 6, 7, 7, 7, 8, 8, 8,
9, 9, 9
};
static PyObject*
digits_stephen(PyObject *self, PyObject *args) {
int num;
if (!PyArg_ParseTuple(args, "i", &num)) return NULL;
unsigned int digits = guess[baseTwoDigits(num)];
return PyLong_FromLong(digits + (num >= tenToThe[digits]));
}
static PyMethodDef digits_methods[] = {
{"intlen", digits_intlen, METH_VARARGS,
"Utilizes the base 10 logarithm to return the number of digits in a number."},
{"bacherikow", digits_bacherikow, METH_VARARGS,
"Utilizes the bacherikow method to return the number of digits in a number."},
{"bisect", digits_bisect, METH_VARARGS,
"Utilizes the bacherikow method to return the number of digits in a number."},
{"bordum", digits_bordum, METH_VARARGS,
"Utilizes the bordum method to return the number of digits in a number."},
{"stephen", digits_stephen, METH_VARARGS,
"Utilizes the stephen method to return the number of digits in a number."},
{NULL, NULL, 0, NULL} /* Sentinel */
};
static struct PyModuleDef digits_module = {
PyModuleDef_HEAD_INIT,
"digits",
NULL,
-1,
digits_methods
};
PyMODINIT_FUNC
PyInit_digits(void)
{
return PyModule_Create(&digits_module);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment