# A function for calculating magnitude order of a number

**URL:** <https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924>\
**Category:** Python Help\
**Created:** [September 9, 2022, 6:49pm UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924 "2022-09-09T18:49:40Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Marco\_Sulla](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/marco_sulla/32/1451_2.png) [@Marco\_Sulla](https://discuss.python.org/u/Marco_Sulla)\
**Post date:** [September 9, 2022, 6:49pm UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/1 "2022-09-09T18:49:40Z")

</div>

What about a function for calculating magnitude order, maybe in math  
module? Something like this:

```auto
def magnitude_order(num):
    if num == 0:
        return 0

    absnum = abs(num)
    order = math.log10(absnum)
    res = math.floor(order)

    return res

```

---

<div class="post-metadata">

**Author:** ![ajoino](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ajoino/32/6907_2.png) [@ajoino](https://discuss.python.org/u/ajoino)\
**Post date:** [September 9, 2022, 7:29pm UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/2 "2022-09-09T19:29:49Z")

</div>

Feels kinda niche, and easy enough to write yourself. Do you find yourself using it often or do you consider it more of a ‘nice to have’ kind of feature?

---

<div class="post-metadata">

**Author:** ![Rosuav](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rosuav/32/3429_2.png) [@Rosuav](https://discuss.python.org/u/Rosuav)\
**Post date:** [September 9, 2022, 8:36pm UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/3 "2022-09-09T20:36:00Z")

</div>

Do you truly need it to be precisely the base 10 log? If not, integers have a bit\_length.

---

<div class="post-metadata">

**Author:** ![Quercus](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/quercus/32/31483_2.png) [@Quercus](https://discuss.python.org/u/Quercus)\
**Post date:** [September 9, 2022, 11:55pm UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/4 "2022-09-09T23:55:47Z")

</div>

Be wary of using logarithms for this computation, notably for large integers close to boundaries of orders of magnitude. See this, for example:

```auto
for exponent in range(11, 20):
    print(exponent, 10 **exponent - 1, magnitude_order(10** exponent - 1))

```

Output:

```
11 99999999999 10
12 999999999999 11
13 9999999999999 12
14 99999999999999 13
15 999999999999999 15
16 9999999999999999 16
17 99999999999999999 17
18 999999999999999999 18
19 9999999999999999999 19

```

Note the increase of `2` in the computed order of magnitude between these two lines of output:

```
14 99999999999999 13
15 999999999999999 15

```

_EDIT:_

To get a closer look at that affected boundary, we can try this:

```auto
for num in range(999999999999996, 1000000000000002):
    print(num, magnitude_order(num))

```

Output:

```
999999999999996 14
999999999999997 14
999999999999998 15
999999999999999 15
1000000000000000 15
1000000000000001 15

```

---

<div class="post-metadata">

**Author:** ![Marco\_Sulla](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/marco_sulla/32/1451_2.png) [@Marco\_Sulla](https://discuss.python.org/u/Marco_Sulla)\
**Post date:** [September 10, 2022, 6:30pm UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/5 "2022-09-10T18:30:27Z")

</div>

I used it and I find it very useful. It’s common in physics to calculate the magnitude order of observables.

---

<div class="post-metadata">

**Author:** ![Marco\_Sulla](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/marco_sulla/32/1451_2.png) [@Marco\_Sulla](https://discuss.python.org/u/Marco_Sulla)\
**Post date:** [September 10, 2022, 6:34pm UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/6 "2022-09-10T18:34:53Z")

</div>

Well, you can add a parameter for the base. I wrote 10 because it’s the most common base for calculating the order of magnitude.

I think that, if the base is two and the value is an integer you can use bit\_length, yes.

---

<div class="post-metadata">

**Author:** ![Marco\_Sulla](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/marco_sulla/32/1451_2.png) [@Marco\_Sulla](https://discuss.python.org/u/Marco_Sulla)\
**Post date:** [September 10, 2022, 6:36pm UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/7 "2022-09-10T18:36:33Z")

</div>

Yes, mine was only an example. Probably CPython already has a better algorithm, since you can format a number in scientific notation.

---

<div class="post-metadata">

**Author:** ![jeff5](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/jeff5/32/1518_2.png) [@jeff5](https://discuss.python.org/u/jeff5)\
**Post date:** [September 10, 2022, 8:11pm UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/8 "2022-09-10T20:11:37Z")

</div>

I think Chris means that, for an integer `k`, the number of decimal digits is `bit_length(k) * log(2)` … rounded up … at least approximately. Close enough for physics.

If you want an answer that is _exactly_ the decimal exponent that `repr(float(k))` would give, that’s quite delicate near boundaries as @Quercus indicates. ISTR this part of the decimal formatting is complicated (and so is the rest of it).

---

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [September 11, 2022, 1:57am UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/9 "2022-09-11T01:57:39Z")

</div>

I think in English, and physics, this is usually called “[order of magnitude](https://en.wikipedia.org/wiki/Order_of_magnitude)”.

---

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [September 11, 2022, 3:02am UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/10 "2022-09-11T03:02:33Z")

</div>

When doing order of magnitude calculations, I think that it is far more common to _round_ the value rather than to _floor_ it. 985 is closer to 1000 than 100, so it should be three orders of magnitude, not two.

But putting that issue aside:

> [@](#):
>
> If you want an answer that is _exactly_ the decimal exponent that `repr(float(k))` would give, that’s quite delicate near boundaries as @Quercus indicates. ISTR this part of the decimal formatting is complicated (and so is the rest of it).

Being delicate and complicated is an argument in favour of doing it right in the stdlib.

The naive algorithm by Marco is sometimes wrong, even for exact powers of ten:

```python
def order(x):
     if x == 0: return 0
     return math.floor(math.log10(abs(x)))

print([i for i in range(1, 100001) if order(10**i) != i])

```

Output is: `[512, 1024, 2048, 32768, 65536]`

For one less than an exact powers of ten it is frequently wrong. Note the change in comparison – the following shows the few times it is correct:

```python
print([i for i in range(1, 100001) if order(10**i - 1) == i-1])

```

Giving results: `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 512, 1024, 2048, 32768, 65536]`

Unfortunately [Stackoverflow gives only the naive algorithm](https://stackoverflow.com/questions/52859751/most-efficient-way-to-find-order-of-magnitude-of-float-in-python).

---

<div class="post-metadata">

**Author:** ![EpicWink](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/epicwink/32/17968_2.png) [@EpicWink](https://discuss.python.org/u/EpicWink)\
**Post date:** [September 11, 2022, 11:15pm UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/11 "2022-09-11T23:15:37Z")

</div>

The orders of magnitude boundaries aren’t at 100, 1000, 10000 and so on, but rather at 10^1.5 (~31), 10^2.5 (~310) etc. Floating point error is very much not important for these conversions. This leads me to think that the OP wants a simple number-of-digits-in-base function

---

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [September 12, 2022, 4:13pm UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/12 "2022-09-12T16:13:49Z")

</div>

> [@](#):
>
> The orders of magnitude boundaries aren’t at 100, 1000, 10000 and so on,

Agreed, it is more usual to round than floor.

> [@](#):
>
> but rather at 10^1.5 (~31), 10^2.5 (~310) etc.

Wait, are you suggesting that 30 is “one order of magnitude” but 32 is “two orders of magnitude”? That surely isn’t right.

---

<div class="post-metadata">

**Author:** ![Rosuav](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rosuav/32/3429_2.png) [@Rosuav](https://discuss.python.org/u/Rosuav)\
**Post date:** [September 12, 2022, 4:40pm UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/13 "2022-09-12T16:40:21Z")

</div>

> [@steven.daprano](#):
>
> Wait, are you suggesting that 30 is “one order of magnitude” but 32 is “two orders of magnitude”? That surely isn’t right.

Why not? There has to be a boundary somewhere. Otherwise, it’s just as weird that 99 is “one” and 101 is “two”. Orders of magnitude aren’t meant to be infinitely granular logarithms.

This does mean there’s a difference between “length of the number” and “order of magnitude”, but only by which direction you round them. We can all agree that 1056 is a much bigger number than 8, and the dispute will only happen when you ask whether or not 60 is a notably bigger number than 20.

---

<div class="post-metadata">

**Author:** ![Quercus](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/quercus/32/31483_2.png) [@Quercus](https://discuss.python.org/u/Quercus)\
**Post date:** [September 12, 2022, 6:07pm UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/14 "2022-09-12T18:07:44Z")

</div>

> [@EpicWink](#):
>
> … This leads me to think that the OP wants a simple number-of-digits-in-base function

@Marco_Sulla, perhaps we need a clarification regarding precisely what you would like this function to do. Please indicate where you would like to place boundaries between ranges of numbers that produce different `return` values. Given your specifications, we can combine our efforts to achieve the desired result, and regardless of whether or not such a function becomes part of the `math` module, as you have proposed, we will have something that is useful to you 🙂

---

<div class="post-metadata">

**Author:** ![EpicWink](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/epicwink/32/17968_2.png) [@EpicWink](https://discuss.python.org/u/EpicWink)\
**Post date:** [September 12, 2022, 10:41pm UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/15 "2022-09-12T22:41:25Z")

</div>

> [@steven.daprano](#):
>
> Wait, are you suggesting that 30 is “one order of magnitude” but 32 is “two orders of magnitude”?

In that case, you’re comparing two numbers, whose relative difference is less than one order of magnitude, so you would say that they both have the same order of magnitude. In isolation from each other, I would be suggesting what you say.

---

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [September 13, 2022, 12:57am UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/16 "2022-09-13T00:57:58Z")

</div>

You seem to be using a definition of “order of magnitude” which is nothing like anything I’ve seen in physics or mathematics. It is certainly nothing like [the way it is taught is Australia](https://mathspace.co/textbooks/syllabuses/Syllabus-810/topics/Topic-18088/subtopics/Subtopic-246306/).

The usual calculation for orders of magnitude is:

- round the number to the nearest exact power of 10;

- the order of magnitude is that power.

E.g. 49 is closer to 10 than 100, so rounds to 10; 51 is closer to 100 than 10, so rounds to 100.

(One might use truncation instead, although I’m not sure why. Orders of magnitude are normally used for rough calculations and estimates. If you estimated a job would come in at $999 would you really truncate it as “$100 as an order of magnitude”? I don’t think so.)

This is equivalent to the exponent part in scientific notation, and to the calculation:

- write the number as `a*10**b`, where `1 <= a < 10`

- the order of magnitude is the power `b`.

Do you have a reference for this unusual definition other than Wikipedia? Wikipedia gives three different definitions, the first agrees with you but its citation to Wolfram Mathworld does not justify the definition given.

The Talk page for the Wikipedia article contains this comment from an anonymous editor:

“This article suffers from a problem that afflicts many minor articles on Wikipedia. It is founded on a definition that exists only on Wikipedia, a definition that was crafted by a Wikipedian editor/author but is not sourced”

and then ironically that editor then goes on to seemingly invent their own unsourced definiton of order of magnitude where the boundaries are `10**(-0.5) <= a < 10**0.5`.

Here are some additional citations showing order of magnitude calculations:

> **[Order of Magnitude Calculator](https://www.omnicalculator.com/math/order-of-magnitude)**
>
> The order of magnitude calculator will show you the number in scientific notation (also called standard form), and based on that, return its order of magnitude.

> **[Order of Magnitude: Definition & Examples - Video & Lesson Transcript...](https://study.com/academy/lesson/order-of-magnitude-definition-examples.html)**
>
> Order of magnitude and scientific notation are important concepts when working with large numbers. Learn about order of magnitude and practice...

> **[1.3: Significant Figures and Order of Magnitude](https://phys.libretexts.org/Bookshelves/University_Physics/Book%3A_Physics_(Boundless)/1%3A_The_Basics_of_Physics/1.3%3A_Significant_Figures_and_Order_of_Magnitude)**
>
> Scientific notation is a way of writing numbers that are too big or too small in a convenient and standard form.

Hmmm… I wonder whether somebody started with the (incorrect) implementation `round(math.log10(x))` and worked backwards to the definition, instead of starting with the definition in terms of place value notation and then coming up with an implementation?

Or possibly confused _logarithmic scales_ with orders of magnitude?

---

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [September 13, 2022, 1:25am UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/17 "2022-09-13T01:25:43Z")

</div>

I have a number of digits function. If nothing else, this discussion has revealed to me that it is buggy!

I _think_ what we need to make this work is a floor logarithm function, one which always truncates instead of being correctly rounded. So something like this might work…

```python
import math

def floor_log(x, base=10):
    if base == 10:
        a = math.log10(x)
    elif base == 2:
        a = math.log2(x)
    else:
        a = math.log(x, base)
    if pow(base, a) > x:
        a -= math.ulp(a)
    return a

def num_digits(num, base=10):
    """Return the number of digits of num in the given base.

    The sign is ignored, and zero is treated as having a single digit.
    """
    num = abs(num)
    if num == 0:
        return 1
    a = floor_log(num, base)
    return math.floor(a) + (pow(base, a) <= num)

```

I have done very little testing on the above, so it may contain bugs.

---

<div class="post-metadata">

**Author:** ![Rosuav](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rosuav/32/3429_2.png) [@Rosuav](https://discuss.python.org/u/Rosuav)\
**Post date:** [September 13, 2022, 1:28am UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/18 "2022-09-13T01:28:55Z")

</div>

> [@steven.daprano](#):
>
> Or possibly confused _logarithmic scales_ with orders of magnitude?

Orders of magnitude ARE logarithmic, by definition. Why is it so illogical to your brain that the boundaries be the geometric means?

---

<div class="post-metadata">

**Author:** ![fungi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/fungi/32/5963_2.png) [@fungi](https://discuss.python.org/u/fungi)\
**Post date:** [September 13, 2022, 1:45am UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/19 "2022-09-13T01:45:31Z")

</div>

Regarding “51 is closer to 100 than 10” what is the definition of  
“closer” used for this? 51 is closer to 100 than to 0, is that what  
you meant? 55 is halfway between 10 and 100, so by simple distance  
51 is closer to 10 than it is to 100.

---

<div class="post-metadata">

**Author:** ![Stefan2](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/stefan2/32/18492_2.png) [@Stefan2](https://discuss.python.org/u/Stefan2)\
**Post date:** [September 13, 2022, 1:57am UTC](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924/20 "2022-09-13T01:57:23Z")

</div>

> [@steven.daprano](#):
>
> [the way it is taught is Australia](https://mathspace.co/textbooks/syllabuses/Syllabus-810/topics/Topic-18088/subtopics/Subtopic-246306/)

Doesn’t look trustworthy. It managed to get all six derived values in its “What if we were to use grams?” table wrong, in different ways. If only it were Wikipedia so we could fix it 😛

I think you say 51 gets rounded to 100 (order 2?), but you also say it’s done as 5.1×10¹ (order 1?), and you say those two ways are equivalent. But they’re not, are they?

Btw I’ve been wanting `math.ilog` for a while, corresponding to `math.isqrt`. Mostly because I’ve seen too many people use some form of `floor(log(n, base))` and I’m tired of showing them how inaccurate it is. Also, that use of `float` not only makes it inaccurate but also limited to a rather small finite range. I think one of the recent int\<-\>str discussions had a short and supposedly fast Python implementation (as part of a referenced “algorithm 1.26”, I think).

[Next page](https://discuss.python.org/t/a-function-for-calculating-magnitude-order-of-a-number/18924.md?page=2)
