Зачем использовать другие базы чисел при программировании


35

Мои коллеги и я изо всех сил пытались понять, почему кто-то изо всех сил старается запрограммировать числа на базе, отличной от базы 10.

Я предположил, что, возможно, вы могли бы оптимизировать более длинные уравнения, поместив переменные в правильную базу, с которой вы работаете (например, если у вас есть только наборы из 5 чего-то без остатков, вы можете использовать базу 5), но я не уверен если это правда

Есть предположения?


6
У вас есть конкретный пример, который поднял этот вопрос? Вещи, которые находятся в base-2 или base-16, очевидно, имеют свои преимущества, так как компьютер легче понять.
KDiTraglia

4
Что должно означать «программирование чисел в базе ...»? Есть цифры. Период. Они внутренне представлены в некоторой базе, но это в основном не имеет значения, и не меняет никаких арифметических правил.

12
@JMD - пожалуйста, поработайте с модераторами, чтобы удалить одну из двух кросс-публикаций, и разместите ее здесь, в P.SE. Перекрестная публикация по сайтам осуждается. Вместо этого моды могут перенести вопросы для вас.

10
@JMD - Кросс-постинг по-прежнему не то, что вы должны делать. Для таких вопросов существует процесс миграции, если это необходимо .
Одед

2
@JMD Не пересекать посты, вопрос, подходящий для более чем одного сайта, встречается крайне редко. На этот раз, например, ваш вопрос был не по теме переполнения стека. Но даже если ваш вопрос был подходящим для обоих сайтов, покупка вашего вопроса вокруг сайтов, как правило, осуждается. Мы все добровольно проводим наше время здесь, вы могли бы, по крайней мере, подождать некоторое время, чтобы оценить ответы, которые вы получили на Переполнение стека, перед перекрестной публикацией.
Яннис

Ответы:


59

Обычная причина написания чисел в коде, отличном от базы 10, заключается в том, что вы немного путаетесь.

Чтобы выбрать пример в C (потому что, если C подходит для чего-либо, это хорошо для битового перемешивания), скажем, некоторый низкоуровневый формат кодирует 2-битное и 6-битное число в байте xx yyyyyy:

main() {
    unsigned char codevalue = 0x94; // 10 010100
    printf("x=%d, y=%d\n", (codevalue & 0xc0) >> 6, (codevalue & 0x3f));
}

производит

x=2, y=20

При таких обстоятельствах запись констант в шестнадцатеричном формате является менее запутанной, чем запись их в десятичном виде, потому что одна шестнадцатеричная цифра соответствует аккуратно четырем битам (полбайта; один 'полубайт') и байту два к одному: число 0x3fимеет все биты установить в низком клеве, и два бита установить в верхнем клеве.

Вы также можете написать вторую строку в восьмеричном виде:

printf("x=%d, y=%d\n", (codevalue & 0300) >> 6, (codevalue & 077));

Здесь каждая цифра соответствует блоку из трех битов. Некоторым людям легче думать, хотя я думаю, что это довольно редко в наши дни.


Другим примером может быть использование «магического числа» 0xDEADBEEF. Смотрите это сообщение stackoverflow.com/questions/5907614/0xdeadbeef-vs-null
Etsitpab Nioliv

45

Основная причина, по которой я использую разные основы - это когда я забочусь о битах.

Это намного легче читать

int mask=0xFF;
byte bottom_byte = value & mask;

чем

int mask=255;
byte bottom_byte = value & mask;

Или изображение что-то более сложное

int mask=0xFF00FF00;
int top_bytes_by_word = value & mask;

в сравнении с

int mask=4278255360; //can you say magic number!? 
int top_bytes_by_word = value & mask;

Здесь очень ясно, каково намерение с шестнадцатеричными примерами, потому что гекс в основном является просто более компактной формой двоичного файла ... В отличие от этого, base-10 (то, что мы используем) не так хорошо отображается в двоичном виде.

0xFF = b11111111 = 255
0xFFFF = b1111111111111111 = 65536
0xF0F0 = b1111000011110000 = 61680

Есть и другие базы, которые вы можете использовать на некоторых языках. Вы найдете очень мало оснований, кроме двоичных, шестнадцатеричных и десятичных. Некоторые странные люди все еще используют восьмеричные, но это самая эзотерическая из всех, что вы можете встретить в здравой программе.


2
Восьмеричное не редкость, 0 - восьмеричное :) (видел, что где-то в сети Stack Exchange, не могу найти его сейчас).
Gerrit

2
@Earlz: люди с большим количеством пальцев. :-)
Брайан Оукли

3
26 x 2 + 10 = все заглавные и строчные буквы и все цифры. Не совсем так необычно. Я также видел, как используется Base 36, и это просто версия без учета регистра.
Даррел Хоффман

3
@vasile: 60 минут в час и 60 секунд в минуту, потому что люди использовали системы Base-60, а не наоборот. Надеюсь, вы не верите, что в природе есть что-то, что говорит, что должно быть 60 минут в час!
Джорен

1
да, они читают это по звездам и используют базу 60, потому что измеряют время. с 360 днями (= 6x60) в году, это не так уж сумасшествие, чтобы измерить время в base-60
с

8

Как вы, наверное, знаете, компьютеры основаны на двоичном коде - это база 2.

Это легко конвертировать между основанием 2 и 4, 8 и 16 (и аналогичной упаковкой 2), и сохранением этого перевода в исходном коде может заставить работать с цифрами намного легче рассуждать о.

Для языков низкого уровня, таких как Assembly и C, это может напрямую переводиться в операции процессора (например, сдвиг битов для деления и умножения), что означает, что использование этих числовых баз приводит к гораздо более быстрому коду.

Кроме того, не все операции являются числовыми операциями - есть битовые карты, в которых вам нужно напрямую поиграть с битами - использование базы 2 или одного из ее кратных для этого значительно упрощает операции.

Если вы хотите узнать больше, я рекомендую прочитать Кодекс Чарльза Петцольда .


3
Компилятору наплевать. Хотя преобразование между базами, которые вы перечисляете, действительно проще, простое (медленное) преобразование для базы 10 также не сложно, и большинство языков, полезных для построения компилятора (вы не используете сборку для этого), имеют это преобразование доступно в их стандартной библиотеке, поэтому оно бесплатно для компиляторов.

1
Использование шестнадцатеричного в C не приводит к более быстрым программам. Компилятору не важно, какую базу вы используете.
Чарльз Сальвиа

5
Независимо от того, на какой базе написана программа, компилятор переводит ее в двоичный файл во время компиляции. Инструкции по сборке идентичны.
Карл Билефельдт

2
Корпоративные компьютеры на самом деле основаны на трехэтапном буле: истина, ложь и «файл не найден»
Мартин Беккет

6

Возможно, если бы вы писали игру, которая имитирует экономику какой-то древней цивилизации, которая использует систему с основанием 12.


4

За исключением узкоспециализированных программ, довольно редко используются базы, отличные от 10, 16 или 2.

База 16 (шестнадцатеричная) полезна просто потому, что полный диапазон байта (0-255) может быть представлен двумя цифрами (0x00-0xFF), что может значительно упростить работу с необработанными шестнадцатеричными дампами или двоичными данными. Шестнадцатеричное также полезно при использовании битовых масок с побитовыми операторами, потому что двузначное соответствие байту помогает с удобочитаемостью.

В более редких случаях основание 2 (двоичное) также может использоваться с побитовыми операциями, но многие языки программирования не поддерживают литералы основания 2, и в любом случае шестнадцатеричное число гораздо более кратко и читабельно.

Base-8 (восьмеричное) также иногда используется из-за прав доступа к файлам UNIX. Кроме этого, довольно редко можно использовать базы, отличные от 10, за пределами узкоспециализированных математических контекстов.


Восьмеричное часто используется для указания символьных значений, а иногда для вывода двоичных данных.
Калеб

3

Наиболее распространенная действительная причина использования других оснований связана с простотой преобразования в базу 2: тривиально преобразовать число базы-8 или базы-16 в двоичное без использования калькулятора путем запоминания короткой таблицы из восьми или шестнадцати. номера:

 0000 0     0001 1     0010 2     0011 3
 0100 4     0101 5     0110 6     0111 7

 1000 8     1001 9     1010 A     1011 B
 1100 C     1101 D     1110 E     1111 F

Это открывает множество возможностей:

  • Когда число представляет собой композицию значимых двоичных чисел, вы можете определить отдельные компоненты без компьютера. Например, если 24-разрядное число представляет цвет в RGB, несложно сказать, что 0xFF00FFэто пурпурный (красный + синий); задача намного сложнее, когда вы представлены16711935
  • Когда число представляет битовую маску, более практично записать ее в виде компактного шестнадцатеричного числа, а не намного более длинного двоичного числа
  • Определенные архитектуры сделали все возможное, чтобы их двоичный код был легко читаемым при печати в виде восьмеричных чисел. PDP-11 была одной из таких систем: самый старший бит позволял бы отличать 8-битные операции от 16-битных; последние две восьмеричные группы позволят вам указать два регистра, участвующих в операции, и так далее. Я знал нескольких людей, которые могли читать двоичный код PDP-11 с экрана без дизассемблера, но им нужно было печатать машинный код в восьмеричной системе.

2

Компьютер (или, точнее, компилятор) совершенно не заботится о том, какую числовую базу вы используете в исходном коде. Наиболее часто используемые языки программирования поддерживают базы 8 (восьмеричное), 10 (десятичное) и 16 (шестнадцатеричное) напрямую. Некоторые из них также поддерживают прямую поддержку двоичных (двоичных) чисел. Специализированные языки могут также поддерживать другие базы номеров. (Под «прямой поддержкой» я подразумеваю, что они позволяют вводить цифры в этой базе, не прибегая к математическим приемам, таким как битовое смещение, умножение, деление и т. Д. В самом исходном коде. Например, C напрямую поддерживает base-16 с его0xпрефикс номера и набор обычных шестнадцатеричных цифр 0123456789ABCDEF. Теперь такие уловки могут быть полезны для облегчения понимания числа в контексте, но до тех пор, пока вы можете выразить одно и то же число без них, делать это - или нет - это просто удобство.)

В конце концов, однако, это несущественно. Допустим, у вас есть следующее утверждение:

int n = 10;

Намерение - создать целочисленную переменную и инициализировать ее десятичным числом 10. Что видит компьютер?

i  n  t     n     =     1  0  ;
69 6e 74 20 6e 20 3d 20 31 30 3b (ASCII, hex)

Компилятор разметит это и поймет, что вы объявляете переменную типа intс именем n, и присваиваете ей некоторое начальное значение. Но что это за ценность?

Для компьютера, и игнорируя проблемы упорядочения и выравнивания байтов, ввод для начального значения переменной является 0x31 0x30. Означает ли это, что начальное значение 0x3130 (12592 в базе 10)? Конечно нет. Синтаксический анализатор языка должен продолжать чтение файла в используемой кодировке символов, поэтому он читает с 1 0последующим ограничителем оператора. Поскольку в этом языке предполагается основание 10, оно читается (в обратном направлении) как «0 единиц, 1 десятков, конец». То есть значение 10 десятичных.

Если мы указали значение в шестнадцатеричном формате, и наш язык использует, 0xчтобы указать, что следующее значение является шестнадцатеричным, то мы получим следующее:

i  n  t     n     =     0  x  1  0  ;
69 6e 74 20 6e 20 3d 20 30 78 31 30 3b (ASCII, hex)

Компилятор видит 0x(0x30 0x78) и распознает его как префикс base-16, поэтому ищет правильный номер base-16, следующий за ним. Вплоть до оператора завершения, он читает 10. Это переводит к 0 «единицам», 1 «шестнадцати», что сработает до 16 в базе 10. Или 00010000 в базе 2. Или, в любом случае, вы хотели бы представить это.

В любом случае, игнорируя оптимизацию ради простоты, компилятор выделяет достаточно памяти для хранения значения intпеременной типа и помещает туда значение, которое он считал из исходного кода, в какую-то временную переменную хранения. Затем (вероятно, намного позже) записывает полученные двоичные значения в файл объектного кода.

Как видите, способ написания числовых значений в исходном коде совершенно несущественен. Это может иметь очень незначительное влияние на время компиляции, но я предположил бы , что (опять же , игнорируя такие оптимизации , такие как кэширование диска в операционной системе) вещи , как случайная турбулентность вокруг вращающихся пластин диска, времени доступа к диску, шина данных столкновений и т. д., имеют гораздо больший эффект.

Итог: не беспокойся об этом. Напишите числа в базе, которую поддерживает выбранный вами язык программирования и которая имеет смысл для того, как число будет использоваться и / или считываться. Вы потратили гораздо больше времени на чтение этого ответа, чем когда-либо восстанавливали время компиляции, будучи умными в отношении того, какую базу чисел использовать в исходном коде. ;)


1

почему кто-то изо всех сил пытался запрограммировать числа на базе, отличной от базы 10.

Вот несколько причин, которые еще не появились ...

x00 - Некоторые ОС и API-интерфейсы аппаратных устройств ожидают, что аргументы будут в шестнадцатеричном / двоичном виде. Когда вы кодируете для таких API, проще использовать числа в том же формате, что и API, вместо того, чтобы конвертировать их между различными базами. Например, чтобы отправить байт конца сообщения на сервер или отправить сообщение, чтобы закрыть соединение с каналом связи.

x01. Возможно, вы захотите, чтобы ваше приложение представляло символы, недоступные на некоторых клавиатурах, например знак авторского права (\ u00a9).

x02 - чтобы некоторые константы / литералы сохранялись (визуально) в разных настройках культуры, особенно когда исходный код / ​​файлы перемещались между разработчиками с разными локальными настройками.

x03 - чтобы их код выглядел запутанным и сложным - хорошо, что C # не поддерживает восьмеричные константы!


1

Ключевой вопрос заключается в представлении одного слова размера компьютера разумным способом. 6502 был 8-битным процессором. 4004 был 4-х битным процессором.

При работе с 4-х или 8-ми битным числом работает хорошо. 4-битное число - это один шестнадцатеричный символ. 8-битное число (байт) - это две шестнадцатеричные цифры. Системы со степенью слова 2 размера являются общепринятыми сегодня стандартами - 16-битными, 32-битными и 64-битными. Все они делятся на 4 для представления в шестнадцатеричном виде.

Octal (основание 8) использовался в системах, где размер слова был 12, 24 или 36. Их использовали PDP8, IBM Mainframe и ICL 1900 дней назад. Эти слова легче представить с использованием октетов, а не с ограниченным шестнадцатеричным диапазоном (да, они тоже делятся на 4).

По-видимому, была также экономия затрат при использовании нумерации Base 8. Представляя 12 битов в BCD, первая цифра может быть только 0-4, но вторая, третья и четвертая могут быть 0-9. Если это было сделано как шестнадцатеричный, у каждого есть 3 шестнадцатеричных символа, но у каждого есть 16 возможных значений. Было дешевле изготовить трубу Никси, у которой было только 0-7, чем ту, которая имела 0-9 (с дополнительной логикой для BCD) или 0-F для шестнадцатеричной.

Сегодня все еще можно увидеть восьмеричное с правами доступа к файлам Unix (755, 644), где каждый владелец, группа и мир имеют 3 бита, представляющих разрешения.


В мире математики иногда делают странные вещи с разными основаниями. Например, слабая последовательность Гудштейна из проекта Эйлера 396 ... или что-то более простое с палиндромными числами . В базе N есть свойство числа, состоящее в том, что число, кратное N - 1, будет иметь сумму, кратную N - 1 . Кроме того, если N - 1 - идеальный квадрат, это свойство также существует для sqrt ( N - 1 ). Это имеет некоторые применения в определенных математических задачах.


1
Восьмеричная причина была в том, что в PDP было 9/18 битных байтов, восьмеричное число представляет 3 биты, поэтому, если ваш байт делится на 3, это имеет большой смысл
Мартин Беккет

1
Octal также использовался в некоторых 16-битных системах (в частности, в PDP-11), потому что 15 - число битов, кроме знакового бита - хорошо делится на 3. Он широко использовался в оригинальной операционной системе UNIX (например, «od» - это стандартный инструмент для вывода двоичных файлов, и его формат по умолчанию - 16-разрядный восьмеричный, а не 8-разрядный шестнадцатеричный), а не только для разрешений. Также может иметь значение, что набор команд PDP-11 имел два поля 6-битных операндов.
Random832

Octal также использовался, потому что он мог отображаться на технологии в то время. Трубы Nexi, кто-нибудь? Или другие 0-9 дисплеи? Отображение автофокуса заняло некоторое время.
Джереми Дж. Старчер

1

В финансовой индустрии существует схема идентификаторов, которая фактически является основой 36 . Он использует цифры 0-9 и буквы BZ для обозначения цифр 0-35. Он пропускает гласные, чтобы предотвратить создание любых отвратительных имен.

Это не идеально, однако. Было время, когда одна несчастная компания имела идентификатор B000BZ.


1

Причина № 1: потому что все цифры на уровне цепи представлены в базе-2 (электрический выключатель включен или выключен). Причина № 2: потому что на одном уровне выше, чем фактические схемы, биты сгруппированы в байты, и байты могут быть легко представлены в виде двух шестнадцатеричных цифр, когда для представления всех возможных значений значения потребуется 3 десятичных знака (и некоторая проверка). байт.

Таким образом, если вы работаете на этих уровнях (или аппроксимируете их в некоторой управляемой среде), проще работать в двоичном или шестнадцатеричном формате, чем в десятичном. Ситуации, в которых вы могли бы это сделать, различны, но обычно это никогда не ситуации, когда вам просто нужна базовая арифметика.


1

Одна из областей, где очень часто используются 16-значные (шестнадцатеричные) числа, - это определение цвета, особенно при использовании HTML / CSS для Интернета. Цвета, которые мы используем на цифровых дисплеях, задаются с помощью комбинации 3 значений интенсивности для 3 «базовых» цветов (RGB - красный, зеленый, синий), которые смешиваются вместе для создания любого из 16 миллионов отображаемых цветов (с использованием 24-битного цвета ).

Например, полная интенсивность зеленого цвета в гексах будет 0x00ff00и 65280в десятичной. Теперь представьте, что вы пытаетесь «вручную» смешать цвет в вашей голове, который имеет равные части красный и синий, скажем, с половинной интенсивностью, чтобы создать красивый фиолетовый цвет :) В шестнадцатеричном виде это будет написано просто так, 0x800080как десятичное значение для этого будет 8388736. Это становится еще проще при работе с оттенками серого - 50% серый 0x808080(шест) и 8421504(десятичное), 75% это 0xC0C0C0и 12632256, и так далее.

Использование hex более интуитивно понятно, и любой, кто знаком с этим использованием цвета, сразу же сможет «угадать» цвет, просто взглянув на значение hex. Это также намного менее подвержено ошибкам, если вам нужно использовать один и тот же цвет несколько раз (что обычно имеет место).

Проверьте любую веб-страницу (и в частности CSS) для сумасшедшего количества использования гексагона: D

ПРИМЕЧАНИЕ. В CSS шестнадцатеричные значения записываются с использованием #префикса, например: #00ff00для зеленого, а также иногда сокращаются до трех цифр, например #0f0для зеленого.


0

Для некоторых алгоритмов база 2 имеет больше смысла, чем что-либо еще. Например, вы бы предпочли написать функцию для обхода бинарного дерева или 10-арного дерева?

Но чаще всего используется база 2, потому что именно так компьютеры почти всегда представляют свои числа. Это значит, что:

  • многие операции более эффективны в базе 2:
    • умножение, деление и по модулю степени 2 намного быстрее, чем общее деление
    • Флаги и небольшие значения могут храниться, извлекаться и более эффективно обрабатываться в виде двоичных цифр большего числа.
  • Операции, которые читают, записывают и манипулируют файлами данных и сетевыми потоками данных, должны иметь дело непосредственно с тем фактом, что они представлены в виде двоичных чисел.

Кроме того, всегда есть редкое приложение, которое по своей природе требует нечетного основания, которое может быть ни 2, ни 10.


2
Конечно, я бы использовал 10-арное дерево. Что за странный 2персонаж вы используете?
CodesInChaos

0

Честно говоря, это предпочтение, если по какой-то причине у вас полидактилия и у вас 11 пальцев или вам нравится считать пальцы на ногах, поэтому вам нравится работать на базе 20, честно, решать вам. Но поймите, что по теме универсальности, большинство из нас, которым приходится иметь дело с битами и байтами ежедневно, будут действительно отмечены галочкой, если мы получим что-то, что делает битовую манипуляцию в базе 19.

ПРИЧИНЫ ДЛЯ ОСНОВАНИЯ x

База 10 - модель всех наших вещей, потому что у нас есть 10 счетных цифр (ноги странные и вонючие, поэтому мы их не используем).

База 2 - Компьютеры используют это для битов (вкл / выкл), это связано с читаемыми уровнями напряжения, распространяемыми затворами / транзисторами / конденсаторами.

База 8 - Старая, когда компьютеры были не очень большими (или назад, когда они были космическими), это было хорошо для чего-то или другого (мне это не нравится)

База 16 - Подходит для отображения верхнего и нижнего полубайтов байта для манипулирования битами. Это очень полезно во встроенном / fpga / аппаратном мире.

НОРМАЛЬНЫЕ ОСНОВЫ В КОМПЬЮТЕРАХ

Если говорить о предпочтениях, я могу точно сказать, как «включен» цвет в шестнадцатеричном значении RGB, которое мне дано, следовательно, оно может быть представлено в виде одного целого в аппаратном обеспечении, а затем с некоторыми сдвигами может быть возвращено мне easy-peasy, 1 сложный цвет = 1 точка данных, которая хороша для обработки больших изображений с ограниченной памятью. Сравните это с базовым представлением 10, вы можете добавить их все и сохранить их в виде числа, но какое число это какое, или, может быть, R - это время 10000, G - это 100, а B - это свое собственное пространство, это много математических операций. Как правило, умножения требуют больше циклов, чем смены, поэтому ваш следующий фрагмент данных уже находится в очереди, прежде чем вы закончите с обработкой последнего фрагмента.

Иногда лучше всего работать на базе 2, 8 или 16. На большинстве машин умножение на 2 - это просто сдвиг, это очень быстро, то же самое с делением на 2.

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

В этом случае назначение целого символа, байта или целого числа для каждого переключателя будет неэффективным и глупым, у переключателя или источника света есть 2 позиции - вкл. И выкл. - зачем мне назначать то, что имеет до 256 позиций, или 2 ^ 16 позиции и т. д. Каждый источник света в массиве может быть 1 битом, подходящим для 8, 16, 32, 64 или 128 (ширина вашего типа данных) для одного слова / регистра. Эффективность пространства необходима и скорее приветствуется.

Использование всего, что основано на 2 ^ n, в программировании для таких вещей, как обработка данных RGB, большого количества данных сигналов - GPS, аудио, ascii и т. Д. - намного проще в шестнадцатеричном, двоичном и восьмеричном виде, поскольку это то, как это представлено в машине, и можно легче распознать, что преподносится и как им манипулировать.

ИСПОЛЬЗОВАНИЕ СТРАННЫХ БАЗ

Там нет эффективности, если вы не код для этого. Вам нужна база 11, вы должны установить для нее тип данных и перегрузить все операторы, чтобы обработать ее представление для пользователя. Я не вижу причин, по которым система, содержащая 5 предметов и только когда-либо умноженная на 5 предметов, должна быть преобразована в математику из пяти предметов. И далее, вам лучше помолиться, чтобы тот, кто решил написать свой код для базы 271, задокументировал это хорошо, или вы могли бы потратить больше времени на его понимание, чем на создание базы 271, поскольку все элементы кратны 271.


0

В древние времена компьютеров у нас было несколько дисплеев, которые могли показывать цифры 0-9, но у нас еще не было AF.

http://ad7zj.net/kd7lmo/images/ground_nixie_front.jpg является одним из таких примеров ...

Octal очень хорошо вписался в эти дисплеи и был проще, чем двоичный или десятичный.


0

Я удивлен, что во всех других ответах не упоминалось два очень распространенных применения в вычислениях для альтернативных основ:

  1. Кодировка : например, кодировка Base64 чрезвычайно распространена. Кодирование просто интерпретирует серию байтов как большое двоичное число (base-2) и преобразует это число в число Base64, представленное цифрами ASCII.
  2. Сжатие : часто желательно представить двоичное, десятичное или шестнадцатеричное число в большей базе, чтобы сократить представление. Например, все сокращатели битов, такие как bit.ly, делают это. Или вы можете сделать это, чтобы сократить GUID для использования в URL.

    - 821F6321-881B-4492-8F84-942186DF059B (base-16 guid) 
    becomes
    - RRIDHW463YD8YXX7MIDI (base-36)
    - 3UFmaWDjj9lifYyuT0 (base-62)
    
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.