# JS Math.log() discrepancy

This should not be problem at all but for the life of me I’m not able to put my finger on the heart of it.

I have a factory that spins out functions to determine the number of digits an integer will have. The factory is supplied a base, which is then scoped to the returned closure. The closure in turn accepts any integer and returns a digit count, mathematically.

Below are three different versions, floor, ceil and parseInt, followed by their output. Can anybody explain what is wrong with my logic? It’s Thanksgiving and I think all the food has clouded my brain. Thanks in advance for your advice.

``````/*
function factory(base) {
let log = Math.log;
let floor = Math.floor;
return x => { return floor(log(x) / log(base)) + 1;};
}
*/
/* floor
999 3
1000 3    // X
1001 4
255 8
256 9
4095 3
4096 4
*/
``````
``````/*
function factory(base) {
let log = Math.log;
let ceil = Math.ceil;
return x => { return ceil(log(x) / log(base));};
}
*/

/* ceil
999 3
1000 3    // X
1001 4
255 8
256 8     // X
257 9
4095 3
4096 3    // X
4097 4
*/
``````
``````function factory(base) {
let log = Math.log;
return x => { return parseInt(log(x) / log(base), 10) + 1;};
}

/* int
999 3
1000 3    // X
1001 4
255 8
256 9
257 9
4095 3
4096 4
4097 4
*/
``````
``````// tests

decDigCount = factory(10);

console.log(999, decDigCount(999));
console.log(1000, decDigCount(1000));
console.log(1001, decDigCount(1001));

binDigCount = factory(2);

console.log(255, binDigCount(255));
console.log(256, binDigCount(256));
console.log(257, binDigCount(257));

hexDigCount = factory(16);

console.log(4095, hexDigCount(4095));
console.log(4096, hexDigCount(4096));
console.log(4097, hexDigCount(4097));

``````

Sandbox: https://repl.it/DsBi

The mistake is in using floats when you need exact results

So I should toss this method, then and just use a string character count?

But then you would need to find a way to represent the value as a string in that base, that may not be possible for large bases… unless you start using unicode or an array of ints or… some such. And if you’re doing that then you’re already counting digits during that process

I’m not sure how integer division is (accurately) done is js. Multiplication should be fine though.

Division and truncation is… probably fine, it’ll either be an exact operation or be greater than the truncated result, and then just truncate it to get the integer result… maybe. I’d feel safer with multiplication without knowing more

1 Like

And so it is…

``````function factory(base) {
let log = Math.log;
return x => { return parseInt(log(x * base) / log(base), 10)};
}
``````
``````999 3
1000 4
1001 4
255 8
256 9
257 9
4095 3
4096 4
4097 4
``````

Same outcome with,

``````function factory(base) {
let log = Math.log;
let floor = Math.floor;
return x => { return floor(log(x * base) / log(base))};
}
``````

I think this method should fail for example for `x = 100`, `x = 100000` and `base = 10`.

I would like to start my post with two notes completely unrelated to the main problem. They will be probably much more valuable than rest of my post (sorry):

1. `parseInt(log(x) / log(base), 10)` does exactly the same thing as `return floor(log(x) / log(base))` as long as `x` and `base` are integers, but `parseInt` is much slower.
2. it’s better to usually define variables with the `const` keyword, `let` is required only if you want to change the value of a variable.

There is nothing wrong with your logic, you just need a more precise function. This is a well-known problem of the numerical methods. We have approximated value of `log(x)` and `log(base)` and then we divide them. Floating-point division of two approximated values intensifies the error.

The situation is different when the `base` is fixed. In this situation, we can create a more precise function based on Taylor series. And in many programming languages, this is done for the most popular bases.

Example (Python 2.7.10):

``````  math.log(1000, 10)
=> 2.9999999999999996
math.log10(1000)
=> 3.0
``````

Is this inconsistency? I would not say so. `log10` is much slower and usually, we do not really need very precise numbers in the science.

There is also a `log10` function in JavaScript. Short quote from the MDN:

This function is the equvalent of `Math.log(x) / Math.log(10)`.

Fortunately, sometimes MDN is wrong. Let’s take a look at language specification:

`Math.log10(x)` returns an implementation-dependent approximation to the base `10` logarithm of `x`.

Implementation-dependent, in most browsers `Math.log10` will return more precise values. Example (Chrome):

``````   Math.log(1000) / Math.log(10)
=> 2.9999999999999996
Math.log10(1000)
=> 3
``````

So you can use `log10`, but if you want to cover all the possible bases and use this code in the web project (different browsers)… well, that’s not really possible. But JavaScript has rather small (for scientific purposes) range for the safe calculations:

``````   Number.MAX_SAFE_INTEGER
=> 9007199254740991
``````

So if you want to stay in this range it might be better to use a recurrence (ECMAScript 6 introduces tail call optimization).

2 Likes

Right you are. Thank you for weighing in, @factoradic.

I was writing this to support another problem involving digits and went out on a limb to see if it could work with multiple bases so moved away from log10 for practice, but my real need is base 10 so will use log10 for that problem.

This will be something to dig into this week.

You can use arbitrary bases, that’s not related to the precision problem here, aside from perhaps if log10 has got “better” behaviour near the edge cases… and then still being at the mercy of the js implementation you’re using

That rules out using anything that doesn’t produce an integer result, which very much includes the log functions.

One way to count digits is to count how many times you need to divide by the base until the value is less than 1. But division isn’t exact so you may be required to start from the other end and count how many times 1 needs to be multiplied by the base to become greater than your value.

1 Like

This is what I’ve settled upon, for now…

``````const digits = x => {return Math.floor(Math.log10(x || 1) + 1)};
``````

For my prpbem I may end up with a longhand approach that caches the digits in an array, the length of which will be the digit count, and the digits will be in isolation, which I need.

``````function num_split(x) {
let r = [];
while (x > 0) {
r.unshift(x % 10);
x = Math.floor(x / 10);
}
return r;
}
``````

Proof of concept:

``````let result = [];
for (let i = 1; i <= 100; i++) {
let r = 0;
let n = num_split(i);
for (let j = 0; j < n.length; j++){
n[j] = Math.pow(n[j], j+1);
r += n[j]
}
if (r === i) {
result.push(r);
}
}
console.log(result)

// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 89 ]
``````