why the inconsistency?
tim.one at comcast.net
Fri Sep 26 02:57:33 CEST 2003
[Christos "TZOTZIOY" Georgiou'
>>> If only math.log(2**64)/math.log(10) worked with longs without
>>> converting them into floats first... :(
>> And after reading the FM, if only
>> def count_digits(x):
>> return int(math.log(x, 10))
>> worked correctly for *all* large values, like 10**70...
> Um, it does :-)
> >>> float(40**200)
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> OverflowError: long int too large to convert to float
> >>> math.log(40**200, 14)
> (10**70 is well within the range of an IEEE double).
You're not used to answering floating-point questions <wink>. It's true
that, in recent Pythons, log(long) isn't limited to arguments that fit in a
machine double. But Christos couldn't have been questioning that, since
10**70 wouldn't have given him an OverflowError even if Python were still so
limited (it used to be).
So the real complaint must be the result his function returns when fed
10**70. The first thing to note is that count_digits(x) doesn't return the
number of decimal digits even for small x:
It's returning what it was told to compute, though, so that's a bug in the
name of the function, or in its implementation. 10**70 has 71 digits in
reality, but from the short examples above we can infer that 70 was "the
So, is that what we get? Can't tell from here: Python is still calling the
platform C math functions, and their accuracy varies across platforms. On
my platform right now,
and that's because
>>> math.log(10**70, 10)
on this box. Going on, and knowing that a C double has 53 bits of precision
on my box,
>>> hex(long(_ * 2**53))
So the result (69.999...) was the closest representable double strictly less
than 70, and that 1-bit rounding error ruins his day <wink>.
So it goes. The math.log() trick can still be extremely useful in getting
the right answer with reasonable speed; we just have to recognize that f.p.
results may suffer small rounding errors, and compensate when they occur:
"""Number of digits in decimal representation of x.
x must be an integer > 0.
# Get close fast.
g = int(math.log(x, 10)) + 1
# Refine. Correct result must have
# 10**(g-1) <= x < 10**g
lower = 10**(g-1)
if x < lower:
print "guess too high"
g -= 1
elif x >= lower*10:
print "guess too low"
g += 1
assert 10**(g-1) <= x < 10**g
guess too low
>>> num_digits(10**70 - 1)
>>> num_digits(10**10000 - 1)
guess too high
all-obvious-to-the-most-casual-observer-ly y'rs - tim
More information about the Python-list