Значение порядка числа
Есть два числа.
- 1.231221200034e12 = 1231221200034.0
- 1.2e-2 = 0.012
При сложении результат будет - 1231221200034.012 незначительное изменение числа, что можно записать как 1.231221200034012e+12. Как можно отследить изменится ли показатель степени "Е" или нет?
Ответы (2 шт):
В общем-то, MaxU уже всё сказал, приложу код для проверки, что порядок сохранится:
import math
x = 1.231221200034e12
y = 1.2e-2
print(int(math.log10(x)) == int(math.log10(x + y)))
Вывод:
True
Предупреждение: правильный ответ уже дан. То что написано тут - упражнение для ума.
Что такое показатель степени после E? Это не такая простая вещь. Формат {:e} выводит число в научном формате:
@>>> '{:e}'.format(9.999999) '9.999999e+00' @>>> '{:e}'.format(9.9999999) '1.000000e+01'
Первая проблема: числа выводятся округлёнными до шести знаков после запятой. Поправить можно подняв точность до 17 знаков. Считается что столько знаков хранится в числе. "Считается" вставлено так как это приближённая оценка.
@>>> '{:.17e}'.format(9.9999999) '9.99999990000000061e+00'
Так лучше.
Показатель степени примерно равен целой части десятичного логарифма числа: int(math.log10(x)). В этой таблице вычислены диапазоны в которых целая часть десятичного логарифма постоянна:
int(math.log10([1.00000000000000019e-01, 9.99999999999999822e+00])) == 0 int(math.log10([1.00000000000000000e+01, 9.99999999999999716e+01])) == 1 int(math.log10([9.99999999999999858e+01, 9.99999999999999318e+02])) == 2 int(math.log10([9.99999999999999432e+02, 9.99999999999999454e+03])) == 3 int(math.log10([9.99999999999999636e+03, 9.99999999999998981e+04])) == 4 int(math.log10([9.99999999999999127e+04, 9.99999999999998836e+05])) == 5 int(math.log10([9.99999999999998952e+05, 9.99999999999998882e+06])) == 6 int(math.log10([9.99999999999999069e+06, 9.99999999999998957e+07])) == 7 int(math.log10([9.99999999999999106e+07, 9.99999999999997735e+08])) == 8 int(math.log10([9.99999999999997854e+08, 9.99999999999997902e+09])) == 9 int(math.log10([9.99999999999998093e+09, 9.99999999999997864e+10])) == 10 ... int(math.log10([9.99999999999983758e+97, 9.99999999999983576e+98])) == 98 int(math.log10([9.99999999999983697e+98, 9.99999999999983503e+99])) == 99
Видно что логарифм слегка занижает результат в большей части случаев. Точность весьма высока (14 знаков) но не идеальна.
Для сравнения таблица когда показатель вычисляется прямой печатью числа:
def ilog10(x):
s = '{:.17e}'.format(x)
m = re.search('e(.*)', s)
return int(m.group(1))
ilog10([1.00000000000000000e+00, 9.99999999999999822e+00]) == 0 ilog10([1.00000000000000000e+01, 9.99999999999999858e+01]) == 1 ilog10([1.00000000000000000e+02, 9.99999999999999886e+02]) == 2 ilog10([1.00000000000000000e+03, 9.99999999999999818e+03]) == 3 ilog10([1.00000000000000000e+04, 9.99999999999999854e+04]) == 4 ilog10([1.00000000000000000e+05, 9.99999999999999884e+05]) == 5 ilog10([1.00000000000000000e+06, 9.99999999999999814e+06]) == 6 ilog10([1.00000000000000000e+07, 9.99999999999999851e+07]) == 7 ilog10([1.00000000000000000e+08, 9.99999999999999881e+08]) == 8 ilog10([1.00000000000000000e+09, 9.99999999999999809e+09]) == 9 ilog10([1.00000000000000000e+10, 9.99999999999999847e+10]) == 10 ... ilog10([1.00000000000000000e+21, 9.99999999999999790e+21]) == 21 ilog10([1.00000000000000000e+22, 9.99999999999999916e+22]) == 22 ilog10([1.00000000000000008e+23, 9.99999999999999983e+23]) == 23 ilog10([1.00000000000000012e+24, 9.99999999999999876e+24]) == 24 ... ilog10([1.00000000000000015e+98, 9.99999999999999967e+98]) == 98 ilog10([1.00000000000000009e+99, 9.99999999999999822e+99]) == 99
Тут точнее. Нижние границы точные до 22. Выше 10^22 степени десятки не представляются точно в вещественном формате.
Показатель определяется точнее с помощью печати числа и последующего чтения из строки. Ситуация странная и, кажется, её просто так не исправить. Стандарт даёт гарантии точности при печати чисел, которые выше точностей при вычислении логарифмов. Тут речь не про стандарт Python, а про стандарт C и IEEE 754.
Но не смотря на это, всегда считайте показатель логарифмом.
Ниже код, который составляет таблицы:
import math
import re
def binary_search(low, high, p):
assert low < high
assert not p(low)
assert p(high)
while True:
middle = low + (high - low) / 2
assert low <= middle <= high
if middle == low or middle == high:
return low, high
if p(middle):
high = middle
else:
low = middle
def ilog10_p(x, p):
fmt = '{{:.{}e}}'.format(p)
s = fmt.format(x)
m = re.search('e(.*)', s)
return int(m.group(1))
def ilog10(x):
assert ilog10_p(x, 17) == ilog10_p(x, 34)
return ilog10_p(x, 17)
def test(prefix, suffix, f):
for e in range(0, 100):
_, low = binary_search(1e-1, 1e101, lambda x: f(x) >= e)
high, _ = binary_search(1e-1, 1e101, lambda x: f(x) > e)
assert f(low) == e
assert f(high) == e
print('{}[{:.17e}, {:.17e}]{} == {:2}'.format(prefix, low, high, suffix, e))
test('int(math.log10(', '))', lambda x: int(math.log10(x)))
test('ilog10(', ')', ilog10)