Представление и хранение чисел в Oracle

dbstalker, 26 июня

Автор статьи: Михайлов Владимир Васильевич, разработчик баз данных Oracle, Москва.

Случайно наткнулся на статьи в Oracle Magazine.Честно говоря, даже не думал, что Oracle может хранить числовой тип данных в своём внутреннем формате, но, как говорится, "век живи - век учись!" Первые две статьи Стива Адамса не вызвали затруднений в понимании, из них можно почерпнуть много полезного в плане оперирования числами Oracle'ом, их округлением и хранением.

Однако, последняя статья под названием "Внутреннее представление типа данных NUMBER" никак не давалась. Приведу один пример. В тексте имеется утверждение: "чтобы не было нулевых байтов, байты положительных чисел сдвинуты на 1" с примером хранения числа 100 в виде десятичной пары "99". Сам собой напрашивается вывод, что единица вычитается. Но далее в таблице представлено хранение числа 12,30 в виде двух пар "13" и "31", откуда должен следовать вывод, что единица всё-таки прибавляется? Объяснение вычисления десятичного порядка я тоже не смог соотнести с приведённым примером. Окончательно разобраться с внутренним хранением чисел заставило предложение "выяснить, почему 110, являющееся меньшим числом, требует для хранения на один байт больше, чем число 1100".

Поиск по ключевым словам "base-100 digits" вывел на Bob Jenkins' Web Site, где приводятся заметки с предположениями именно такого, а не другого вида хранения чисел. Однако, не удовлетворившись, решил поискать ещё немного, и вышел на: документацию Oracle: здесь и здесь Больше доверяя документации, из первой я вынес то же, что и из вышеупомянутых статей Стива Адамса, а вторая, кроме всего прочего, закрепила во мне уверенность, что единица всё-таки прибавляется! И стало ясно, почему для хранения числа 110 требуется три байта, а для 1100 - два. Попутно узнал, что представление и оперирование числами по основанию, отличному от двоичного, но кратного десятичному, называется "Арифметикой длинных чисел", статья С.М.Окулова по ссылке может помочь в ознакомлении с подобным представлением чисел.

Далее я изложу своё видение внутреннего хранения чисел Oracle на основании всех вышеупомянутых источников (но в основном - на документаци Oracle), а также использования функций vsize() для проверки размера хранения числа в байтах и dump() для получения наглядного результата.

Итак, Oracle хранит числовой тип данных в виде <Длина><Содержимое>, где <Длина> - размер в байтах хранимой величины, <Содержимое> - собственно хранимая величина, в которую входят до 21 байта данных в специальном формате. Прояснение этого "специального формата" и является целью всей данной статьи.

Во-первых, числовая величина хранится в нормализованном виде по основанию 100. (Далее понятие "нормализованный вид" будет подразумевать "по основанию 100") Иными словами, десятичная точка переносится по паре разрядов влево или вправо до одной значащей цифры или до первой пары значащих цифр. Если перенос происходит вправо, то все нули слева от пары значащих цифр отбрасываются, впрочем, как и все нули справа. Количество шагов в переносе на два разряда будет учтён в последующем при вычислении степени числа (см. далее).

Во-вторых, первый байт включает в себя знак числа (первый бит этого байта, равный единице в случае положительного числа, и наоборот) и показатель степени (десятичную экспоненту). Для положительных чисел к подсчитанному количеству шагов (см. выше) степени нормализованного числа прибавляется 65 (в десятичном представлении), затем прибавляется 128 (таким образом появляется единица в первом бите), полученный результат размещается в первом байте. Для отрицательных чисел проделывается всё то же самое, а после операций сложения полученное значение инвертируется. (В документаии Oracle приводится абзац с полным расчётом первого байта хранимого числа). То есть, скажем, представление нулевой степени положительного числа 193 отличается от представления нулевой степени отрицательного числа 62, от этих значений и можно вести отсчёт при вычислении хранимой степени числа. Нуль хранится в виде одного байта со значением 0x80 (128 в десятичном представлении).

В-третьих, что касается значащих цифр (мантиссы) числа. Oracle отводит под неё до 20 байт и хранит их в представлении, удобном для проведения арифметических операций с "длинными числами". В нашем (то есть, его, Oracle'а) случае перед побайтным размещением числа отсчитывается (и здесь не забываем про base-100 digits) пара разрядов влево и вправо от десятичной точки. К каждой такой паре цифр, рассматривая эту пару как десятичное число, прибавляется единица, если число положительное, либо оно вычитается из 101, если число отрицательное. Если в самом крайнем положении (левом или правом) остаётся одна цифра, то она дополняется нулём (слева или справа соответственно), и перед прибавлением единицы или вычитании из 101 эта пара цифр рассматривается как десятичное число.

После рассмотренного первого байта, который определяет знак и степень числа, идут байты с парами цифр, в которых размещаются результаты приведённых вычислений для пар цифр мантиссы.

Кроме того, если длина отрицательного числа меньше 20 байт, то в последнем байте хранится число 102. Имеется предположение, что это сделано для упрощения сравнения отрицательных чисел (см. ссылку на Bob Jenkins' Web Site).

Приведённое объяснение, на мой взгляд, может послужить методом, позволяющим выяснить, в каком виде будет храниться и какой размер будет занимать величина числового типа данных Oracle.

Чтобы стало понятнее, лучше показать, как это выглядит наглядно. Ниже будут приведены только само число и формат хранения числа в виде последовательности байтов, начиная с первого байта (но без байта <Длина>) в десятичном представлении. Допустим, что мы сохраняем числа +123456.900, -123.0007, +1.000002, +0.00000123, -23.746*10^-15, +110, +1100.

Нормализованная форма +123456.900 есть +12.3456900*100^2.Байтовое хранение: (195|13|35|57|91)

Нормализованная форма -123.0007 есть -1.230007*100^1.

Байтовое хранение: (61|100|78|101|94|102)

Нормализованная форма +1.000002 есть +1.000002*100^0.

Байтовое хранение: (193|02|01|01|03)

Нормализованная форма +0.00000123 есть +1.23*100^-3.

Байтовое хранение: (190|02|24)

Нормализованная форма -23.746*10^-15 есть -2.3746*100^-7.

Байтовое хранение: (69|99|64|55|102)

Нормализованная форма +110 есть +1.10*10^1.

Байтовое хранение: (194|02|11)

Нормализованная форма +1100 есть +11*100^1.

Байтовое хранение: (194|12)

Теперь можно пофантазировать на тему, как происходит сравнение чисел в Oracle. Скажем, сравним числа 1, -1 и -1.01. В представлении Oracle вместе с предшествующим байтом <Длина> они имеют вид соответственно: (2|193|02), (3|62|100|102) и (4|62|100|100|102). Сравним 1 и -1. Логично в самом начале сравнить байты хранения степени (которые называли "первыми", но здесь они представлены вторыми по счёту после байта длины хранения числа): 194 > 62, этого оказывается достаточно, и получаем 1 > -1. Сравним -1 и -1.01. Начнём аналогично: 62 = 62, следовательно, нужна дальнейшая проверка. Будем сравнивать побайтно: 100 = 100. Далее: 102 > 100, получаем -1 > -1.01, что верно. По-видимому, предположение о наличии байта со значением 102 у отрицательных чисел для упрощения их сравнения - верно. Проверка отрицательных чисел, занимающих все 20 отведённых байт, по всей видимости, проводится иным способом (однако, и встречаемость подобной точности весьма маловероятна).

Эта статья - исправленная версия первой. Опыт её написания, несомненно, окажет влияние на моё отношение к авторитетам в мире Oracle как в их непосредственном лице, так и в лице уважаемого журнала. Как сказал один мой друг применительно к несколько иной ситуации, но я его перефразирую по случаю: "авторитетам можно доверять, но на них нельзя полагаться!"

 

Новый комментарий

Я не спамер: введите суму 8+4



 

От авторов блога

О Блоге - прочитай перед началом.

Задать вопрос и получить ответ - уже решено 94 вопросов

Глоссарий - список терминов и сокращений


 
 

Бизнес форум

Последние темы:

Микрофон
19 августа, 2 ответа
Сумочка
19 августа, 2 ответа
средства для рук
17 августа, 3 ответа