LibMPUIO (Multiple Precision Input/Output)
LibMPUIO – библиотека ввода‑вывода для программ, использующих LibMPU и числа большой разрядности. По устройству интерфейса она близка к стандартному stdio, в библиотеке есть собственный тип потока mpu_FILE, стандартные потоки, файловый ввод‑вывод, буферизация, позиционирование, printf/scanf, memory streams, popen, cookie streams и _unlocked‑варианты функций.
Главная особенность LibMPUIO в том, что она объединяет в одном интерфейсе обычные типы языка C и объекты LibMPU – целые, вещественные и комплексные числа произвольной точности. При этом библиотека использует строгую и предсказуемую модель текста: внутри текст представлен UCS‑2, а в реальных файлах, терминале и pipe используется UTF‑8.
tags
В результате в программе на C можно одинаково естественно писать:
mpu_printf( MPU_UCS2( "value = %d\n" ), 42 );
и, например, выводить 4096‑битное целое или 16384‑битное вещественное число:
mpu_printf( MPU_UCS2( "n = %#Z4096x\n" ), n ); mpu_printf( MPU_UCS2( "r = %.64.4Z16384R\n" ), r );
Ниже рассмотрим модель представления текста, потоки и основные форматы ввода‑вывода LibMPUIO.
Модель представления текста
Внутреннее представление текста LibMPUIO основано на типе __mpu_char16_t и строгом UCS‑2:
Для реального потока преобразование выполняется автоматически:
input : UTF-8 bytes -> strict decoder -> UCS-2 characters output: UCS-2 characters -> UTF-8 encoder -> bytes
Строгий UCS‑2 означает, что один элемент __mpu_char16_t представляет один символ из Basic Multilingual Plane (BMP). Surrogate‑коды U+D800...U+DFFF не являются допустимыми символами, а Unicode scalar values выше U+FFFF нельзя представить одним элементом внутренней строки.
Для строковых литералов используется макро MPU_UCS2():
__mpu_char16_t text[] = MPU_UCS2( "Привет" ); mpu_printf( MPU_UCS2( "%s\n" ), text );
MPU_UCS2() – это не runtime‑конвертер. Макро создаёт 16‑битный строковый литерал непосредственно в исходном коде.
В библиотеке одновременно встречаются три символьных типа:
| Тип | Значение |
|---|---|
| __mpu_char8_t | байтовые строки, UTF‑8 строки или строки текущей locale – смысл зависит от API |
| __mpu_char16_t | внутренние строки LibMPUIO, strict UCS‑2 |
| __mpu_char32_t | отдельный Unicode scalar в UTF‑8 string API |
Особенно важно не считать любой __mpu_char8_t * автоматически UTF‑8. Например, семейство mpu_str8* работает с байтами, mpu_utf8* – с UTF‑8, а %a интерпретирует строку в соответствии с текущей LC_CTYPE locale.
Потоки LibMPUIO
LibMPUIO имеет собственный тип потока:
mpu_FILE *fp;
и собственные стандартные потоки:
mpu_stdin mpu_stdout mpu_stderr
Они не являются объектами FILE * из Libc и не должны смешиваться с stdin, stdout и stderr.
Обычный текстовый файл можно создать следующим образом:
mpu_FILE *fp; fp = mpu_fopen( "hello.txt", "w" ); if( fp == NULL ) return 1; mpu_fprintf( fp, MPU_UCS2( "Hello, Андрей!\n" ) ); mpu_fclose( fp );
Строка формата в исходном коде представлена в UCS‑2, а в файл с именем hello.txt будет записан UTF‑8.
Семейство функций LibMPUIO stream API, для пользователя, во многом знакомо по stdio стандартной библиотеки языка C:
mpu_fopen() mpu_fclose() mpu_fgetc() mpu_fputc() mpu_fgets() mpu_fputs() mpu_fread() mpu_fwrite() mpu_fseek() mpu_ftell() mpu_fseeko() mpu_ftello() mpu_fflush() mpu_setvbuf() mpu_feof() mpu_ferror() mpu_clearerr()
Кроме обычных файлов имеются динамические и фиксированные memory streams, пользовательские cookie streams и command pipes.
Форматированный вывод
К семейству функций форматированного вывода относятся:
mpu_printf() mpu_vprintf() mpu_fprintf() mpu_vfprintf() mpu_sprintf() mpu_vsprintf() mpu_snprintf() mpu_vsnprintf() mpu_asprintf() mpu_vasprintf() mpu_dprintf() mpu_vdprintf()
Строка формата всегда имеет тип:
const __mpu_char16_t *
Например:
mpu_printf( MPU_UCS2( "value = %d\n" ), value );
В таблицах ниже символ '·' обозначает пробел. Границы форматируемого поля специально показаны символами '|', чтобы ширина и выравнивание были видны непосредственно в примерах.
UCS‑2 Строки
В отличие от GNU Libc, "%s" в LibMPUIO принимает не char *, а UCS‑2 строку:
__mpu_char16_t s[] = MPU_UCS2( "abc" );
| Формат | Результат |
|---|---|
| %s | |abc| |
| %10s | |·······abc| |
| %-10s | |abc·······| |
| %.2s | |ab| |
| %10.2s | |········ab| |
| %-10.2s | |ab········| |
Например:
mpu_printf( MPU_UCS2( "|%10s|\n" ), s );
выводит поле шириной десять UCS‑2 символов.
UTF‑8 Строки
Обычно строковые константы содержат символы согласно текущей локали. Если в вашей системе настроена локаль, например, ru_RU.UTF‑8, то строковые константы в вашей программе интерпретируются как строки содержащие UTF‑8 символы.
Здесь и далее мы будем подразумевать, что все мультибайтовые последовательности символов имеют UTF‑8 формат.
Формат "%a" является мостом к 8‑битной multibyte‑строке текущей LC_CTYPE locale:
__mpu_char8_t s[] = "Андрей"; mpu_printf( MPU_UCS2( "|%10a|\n" ), s );
При UTF‑8 locale результат будет:
|····Андрей|
То есть ширина поля измеряется после преобразования в UCS‑2 символов, а не в исходных байтах UTF‑8.
| Формат | Результат |
|---|---|
| %a | |Андрей| |
| %10a | |····Андрей| |
| %-10a | |Андрей····| |
| %.3a | |Анд| |
| %10.3a | |·······Анд| |
Важно: "%a" здесь не является C99 hex‑float преобразованием. В LibMPUIO строчная "%a" зарезервирована для current‑locale multibyte string.
Обычные целые числа
Для обычных C integer types поддерживаются привычные преобразования:
%d %i %u %o %x %X
а также двоичные %b/%B.
Пусть:
int n = 12345;
| Формат | Результат |
|---|---|
| %d | |12345| |
| %10d | |·····12345| |
| %-10d | |12345·····| |
| %+10d | |····+12345| |
| % 10d | |·····12345| |
| %010d | |0000012345| |
| %.8d | |00012345| |
| %10.8d | |··00012345| |
Для hexadecimal используются обычные правила alternate form:
unsigned int h = 0xabcd;
| Формат | Результат |
|---|---|
| %x | |abcd| |
| %X | |ABCD| |
| %#x | |0xabcd| |
| %#X | |0XABCD| |
| %#010x | |0x0000abcd| |
Двоичный формат
LibMPUIO расширяет привычный набор integer conversions двоичным выводом:
mpu_printf( MPU_UCS2( "%#b\n" ), 13u ); mpu_printf( MPU_UCS2( "%#B\n" ), 13u );
Результат:
0b1101 0B1101
Флаг # добавляет 0b или 0B.
Обычные вещественные числа
Обычные %e/%E/%f/%F/%g/%G работают с C double/long double и следуют активной LC_NUMERIC так же, как formatter Libc.
Для:
double x = 12.34567;
получаем:
| Формат | Результат |
|---|---|
| %f | |12.345670| |
| %.2f | |12.35| |
| %10.2f | |·····12.35| |
| %-10.2f | |12.35·····| |
| %+10.2f | |····+12.35| |
| %.3e | |1.235e+01| |
| %+12.3e | |··+1.235e+01| |
Эти преобразования не следует смешивать с MPU real formats: у них разные типы аргументов и разная грамматика.
LibMPU Целые
Главное расширение format string – модификатор размера:
z<bits> Z<bits>
Строчная и прописная формы эквивалентны. Например:
mpu_int128_t n; iatoi( n, (__mpu_char8_t *)"12345", NB_I128 ); mpu_printf( MPU_UCS2( "%Z128d\n" ), n );
Z128 сообщает variadic formatter-у, что аргумент является объектом LibMPU соответствующей разрядности.
Это необходимо потому, что типы LibMPU обычно представлены массивами: имя объекта преобразуется в pointer, но из самого va_list нельзя узнать размер pointed storage.
Для MPU integers доступны signed/unsigned decimal, octal, hexadecimal и binary преобразования:
mpu_printf( MPU_UCS2( "%Z128d\n" ), n ); mpu_printf( MPU_UCS2( "%Z128u\n" ), n ); mpu_printf( MPU_UCS2( "%#Z128x\n" ), n ); mpu_printf( MPU_UCS2( "%#Z128b\n" ), n );
Пример форматирования 12345:
| Формат | Результат |
|---|---|
| %Z128d | |12345| |
| %20Z128d | |···············12345| |
| %-20Z128d | |12345···············| |
| %+20Z128d | |··············+12345| |
| %020Z128d | |00000000000000012345| |
| %20.10Z128d | |··········0000012345| |
Тот же механизм работает и с большими объектами:
mpu_int1024_t i1024; mpu_int4096_t i4096; mpu_int65536_t i65536; mpu_printf( MPU_UCS2( "%#Z1024b\n" ), i1024 ); mpu_printf( MPU_UCS2( "%#Z4096x\n" ), i4096 ); mpu_printf( MPU_UCS2( "%Z65536u\n" ), i65536 );
Если цифры после z/Z опущены, по умолчанию используется MPU size 128 bits. Поэтому %zu в LibMPUIO не означает size_t: стандартный C length modifier z намеренно отдан LibMPU.
LibMPU Вещественные
Для MPU real objects поддерживаются преобразования r/R и формы e/E/f/F/g/G с модификатором z/Z<bits>.
Пусть:
mpu_real128_t r; ascii_to_real( r, (__mpu_char8_t *)"12345.6789", NB_R128 );
Тогда:
| Формат | Результат |
|---|---|
| %Z128R | |1.234568E+4| |
| %.8Z128R | |1.23456789E+4| |
| %.4.3Z128e | |1.2346e+004| |
| %20.4.3Z128e | |·········1.2346e+004| |
| %-20.4.3Z128e | |1.2346e+004·········| |
| %20.6Z128g | |·············12345.7| |
| %+20.6Z128g | |············+12345.7| |
Первая precision задаёт число цифр мантиссы:
%.8Z128R
Вторая precision – расширение LibMPUIO .expdigits – задаёт минимальное количество цифр экспоненты:
%.4.3Z128e -> 1.2346e+004
То есть:
| .4 | mantissa precision |
| .3 | minimum exponent width |
| Z128 | размер MPU object |
| e | scientific conversion |
Родные для LibMPU LibMPUIO %r/%R являются именами преобразований, но печатным разделителем экспоненты для вещественных чисел остаются e/E.
Ещё одна историческая особенность: MPU f/F являются псевдонимами e/E, а не fixed-point форматами:
%.4.3Z128e -> 1.2346e+004 %.4.3Z128f -> 1.2346e+004
Для g/G действует general-format логика. Флаг '#' сохраняет десятичную точку и конечные нули дробной части:
%.8Z128g -> 12.5 %#.8Z128g -> 12.500000
Флаг '+' работает и с MPU real values. Например:
mpu_printf( MPU_UCS2( "|%+20.6Z128g|\n" ), r );
даёт:
|············+12345.7|
Полный формат удобно читать слева направо:
%+020.8.4Z128R
где:
| + | всегда выводить знак |
| 0 | заполнять поле нулями |
| 20 | минимальная ширина поля |
| .8 | mantissa precision |
| .4 | minimum exponent width |
| Z128 | mpu_real128_t |
| R | uppercase native MPU real conversion |
Фактический результат:
|+0001.23456789E+0004|
Знак остаётся перед незначащими нулями.
LibMPU Комплексные
Для вывода комплексных переменных используется преобразование %j/%J:
mpu_real128_t re, im; mpu_complex128_t c; ascii_to_real( re, (__mpu_char8_t *)"1.25", NB_R128 ); ascii_to_real( im, (__mpu_char8_t *)"-2.5", NB_R128 ); c_gen_complex( c, re, im, NB_C128 ); mpu_printf( MPU_UCS2( "%.6.3Z128J\n" ), c );
Результат:
1.250000R+000-2.500000J+000
В формате %.6.3Z128J:
| .6 | precision каждой компоненты |
| .3 | minimum exponent width |
| Z128 | mpu_complex128_t |
| J | uppercase complex representation |
Для %J real и imaginary компоненты используют исторические маркеры R и J; для %j – r и j.
Ширина и выравнивание применяются ко всему complex представлению:
| Формат | Результат |
|---|---|
| %.6.3Z128J | |1.250000R+000-2.500000J+000| |
| %40.6.3Z128J | |·············1.250000R+000-2.500000J+000| |
| %-40.6.3Z128J | |1.250000R+000-2.500000J+000·············| |
Флаг '+' также поддерживается для представления комплексных чисел. Для положительного вещественного компонента можно явно запросить ведущий знак:
mpu_printf( MPU_UCS2( "|%+.6.3Z128J|\n" ), c );
Для 1.25 + 2.5j результат выглядит так:
|+1.250000R+000+2.500000J+000|
Zero padding сохраняет ведущий знак перед незначащими нулями:
| Формат | Результат |
|---|---|
| %+040.6.3Z128J | |+0000001.250000R+000-0000002.500000J+000| |
| %+039.6.3Z128J | |·+000001.250000R+000-000002.500000J+000| |
| %-039.6.3Z128J | |1.250000R+000-2.500000J+000············| |
Ширина, точность, выравнивание
Основные элементы format specification выглядят так:
%[flags][width][.precision][.expdigits][length-or-Zbits]conversion
Основные флаги:
| Элемент | Значение |
|---|---|
| - | выравнивание влево |
| + | всегда выводить знак для signed numeric conversions |
| space | пробел перед положительным signed value; + имеет приоритет |
| # | alternate form |
| 0 | zero padding, когда применимо |
| width | минимальная ширина поля |
| .precision | precision числа или максимум символов строки |
| .expdigits | дополнительная минимальная ширина exponent у MPU real/complex |
| z/Z<bits> | размер и типовой контракт MPU object |
Width и precision можно задавать через *:
mpu_printf( MPU_UCS2( "|%*d|\n" ), 10, 12345 ); mpu_printf( MPU_UCS2( "|%*.*f|\n" ), 12, 3, 12.34567 );
Результат:
|·····12345| |······12.346|
Отрицательная ширина эквивалентна положительной ширине вместе с -.
Практический пример настоящей числовой таблицы:
mpu_printf( MPU_UCS2( "%-16s | %24s | %24s\n" ),
MPU_UCS2( "Variable" ),
MPU_UCS2( "Value" ),
MPU_UCS2( "Hex" ) );
mpu_printf( MPU_UCS2( "-----------------|--------------------------|--------------------------\n" ) );
mpu_printf( MPU_UCS2( "%-16s | %24Z128d | %#24Z128x\n" ),
MPU_UCS2( "counter" ), value, value );
Вывод:
Variable | Value | Hex -----------------|--------------------------|-------------------------- counter | 12345 | 0x3039
Форматированный ввод
Семейство функций форматированного ввода:
mpu_scanf() mpu_vscanf() mpu_fscanf() mpu_vfscanf() mpu_sscanf() mpu_vsscanf()
Строка формата также рассматривается как UCS‑2 последовательность.
Для файлового потока входные UTF‑8 байты сначала декодируются в UCS‑2 символы, после чего применяется анализатор.
Возвращаемое значение соответствует привычной scanf‑модели: это число успешно выполненных присваиваний. %n и suppressed conversion %*... не увеличивают количество присваиваний.
Строки и обычные типы
Основные строковые преобразования:
| Format | Destination |
|---|---|
| %c | __mpu_char16_t * |
| %s | __mpu_char16_t * |
| %a | __mpu_char8_t *, current-locale multibyte string |
| %[...] | __mpu_char16_t *, UCS‑2 scanset |
Например:
__mpu_char16_t text[] = MPU_UCS2( "42 hello" );
__mpu_char16_t word[64];
int value;
if( mpu_sscanf( text, MPU_UCS2( "%d %s" ), &value, word ) == 2 )
{
mpu_printf( MPU_UCS2( "value = %d; word = '%s'\n" ),
value, word );
}
Для current-locale multibyte destination используется %a:
__mpu_char8_t word8[64];
if( mpu_sscanf( MPU_UCS2( "Андрей" ),
MPU_UCS2( "%a" ), word8 ) == 1 )
{
/* word8 содержит multibyte string текущей LC_CTYPE locale. */
}
Как и у обычного scanf, для %s и %a следует задавать field width, если размер destination ограничен.
Ввод Целых
При вводе используется все тот же z/Z<bits> модификатор:
mpu_int128_t value;
if( mpu_sscanf( MPU_UCS2( "0xabcd" ),
MPU_UCS2( "%Z128x" ), value ) == 1 )
{
mpu_printf( MPU_UCS2( "%#Z128x\n" ), value );
}
Результат:
0xabcd
Доступны следующие MPU integer преобразования:
%d %i signed decimal %u unsigned decimal %o octal %x %X hexadecimal %b %B binary
с добавленным z/Z<bits>:
%Z256d %Z1024u %Z4096x %Z8192b
Ввод Вещественных
При вводе вещественных чисел поддерживается MPU %R/%r и обычные %e/%E/%f/%F/%g/%G преобразования с модификатором размерности z/Z<bits>.
Для 128‑битного объекта:
%Z128R -> mpu_real128_t %Z128E -> mpu_real128_t
Обе формы используют один и тот же способ преобразования. Например:
mpu_real128_t r1, r2;
mpu_sscanf( MPU_UCS2( "1.23456789E+10" ),
MPU_UCS2( "%Z128R" ), r1 );
mpu_sscanf( MPU_UCS2( "1.23456789E+10" ),
MPU_UCS2( "%Z128E" ), r2 );
mpu_printf( MPU_UCS2( "R: %Z128R\n" ), r1 );
mpu_printf( MPU_UCS2( "E: %Z128R\n" ), r2 );
Результат:
R: 1.234568E+10 E: 1.234568E+10
Сканнер LibMPUIO намеренно строже, чем низкоуровневое преобразование ascii_to_real() библиотеки LibMPU. Для мантиссы требуется хотя бы одна десятичная цифра.
Корректные примеры чисел:
0 +0 -0 .0 3.14 123. -3.14e+17 1.25e-003
Некорректные формы вещественных:
. +. .e0 1e 1e+
При незавершённом сканировании выполняется rollback и сканнер не должен без необходимости «съедать» символы, принадлежащие следующему числу.
Ввод Комплексных
Для ввода комплексных чисел используется преобразование %j/%J с модификатором z/Z<bits>.
Примеры допустимой грамматики:
1r0 -1r0 1.25r+3+2.5j-4 1.25r+3 +2.5j-4 -2.5j-4 0r0+0j0 1r0 2j0
Например:
mpu_complex128_t c;
if( mpu_sscanf( MPU_UCS2( "1.25r+3+2.5j-4" ),
MPU_UCS2( "%Z128J" ), c ) == 1 )
{
mpu_printf( MPU_UCS2( "%.4.3Z128J\n" ), c );
}
Результат:
1.2500R+003+2.5000J-004
Если один из компонентов комплексного числа отсутствует, сканнер формирует для него нулевое значение в соответствии с граматикой преобразования %j/%J.
Text I/O и raw I/O
В LibMPUIO принципиально разделены текстовые и «сырые» операции.
Text API работает с UCS‑2 символами и выполняет UTF‑8 преобразование на реальном внешнем backend:
mpu_fgetc() mpu_fputc() mpu_fgets() mpu_fputs() mpu_fprintf() mpu_fscanf()
Raw I/O:
mpu_fread( ptr, size, nmemb, fp ); mpu_fwrite( ptr, size, nmemb, fp );
передаёт байты без перекодировки.
Поэтому функцию mpu_fread() нельзя использовать как замену UTF‑8 декодера. Если задача текстовая – следует использовать Text API. Если читается бинарный протокол или структурированный byte stream – Raw API.
У единиц измерения также есть важное различие:
formatted return count -> UCS-2 characters real file position -> external UTF-8 byte offset memory stream position -> UCS-2 code units mpu_fread()/mpu_fwrite() -> bytes
Memory streams, cookie streams и pipe
LibMPUIO не ограничивается обычными файлами.
Fixed UCS-2 memory stream
__mpu_char16_t storage[128]; mpu_FILE *fp; fp = mpu_fmemopen( storage, 128, "w+" ); if( fp == NULL ) return 1; mpu_fprintf( fp, MPU_UCS2( "value=%d" ), 42 ); mpu_fflush( fp ); mpu_printf( MPU_UCS2( "storage = '%s'\n" ), storage ); mpu_fclose( fp );
Размер 128 здесь означает число UCS‑2 элементов, а не байтов.
Dynamic UCS-2 memory stream
__mpu_char16_t *text = NULL; size_t length = 0; mpu_FILE *fp; fp = mpu_open_memstream( &text, &length ); mpu_fprintf( fp, MPU_UCS2( "name=%s" ), MPU_UCS2( "mpu" ) ); mpu_fflush( fp ); /* text и length опубликованы после flush. */ mpu_fclose( fp ); free( text );
Cookie streams
Функция mpu_fopencookie() позволяет построить mpu_FILE поверх пользовательских callback‑функций. Callback‑функции работают с байтами. Если поверх такого потока вызывается Text API, LibMPUIO кодирует UCS‑2 в UTF‑8 перед вызовом callback‑функции записи (write) и декодирует UTF‑8 после вызова callback‑функции чтения (read).
Command pipe
mpu_FILE *fp;
fp = mpu_popen( "printf 'hello\\n'", "r" );
if( fp != NULL )
{
__mpu_char16_t line[32];
mpu_fgets( line, 32, fp );
mpu_printf( MPU_UCS2( "%s" ), line );
mpu_pclose( fp );
}
Для Text API pipe bytes рассматриваются как UTF‑8.
Сборка
LibMPUIO использует GNU Autotools и зависит от установленной LibMPU.
При сборке LibMPU можно использовать, например:
mkdir build-libmpu cd build-libmpu ../libmpu/configure \ --prefix=/usr \ --libdir=/usr/lib64 \ --with-real-math-f-limit=16384 \ --enable-static=no make make DESTDIR="$PKG" install
Опция --with-real-math-f-limit=16384 относится только к LibMPU.
LibMPUIO конфигурируется отдельно и этой опции не имеет:
mkdir build-libmpuio cd build-libmpuio ../libmpuio/configure \ --prefix=/usr \ --libdir=/usr/lib64 \ --enable-static=no make make DESTDIR="$PKG" install
При пакетной сборке DESTDIR используется как staging root; конечный prefix при этом остаётся /usr.
Для сборки прикладной программы удобно использовать скрипт mpuio-config:
CC = gcc CFLAGS = `mpuio-config --cflags` LDFLAGS = `mpuio-config --ldflags` LIBS = `mpuio-config --libs` -lpthread SOURCES = main.c OBJECTS = main.o all: main .SUFFIXES: .SUFFIXES: .o .c %.o : %.c $(CC) $(CFLAGS) -c -o $@ $< main: $(OBJECTS) $(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBS) clean: rm -f main *.o
Примеры программ
Ниже приведена небольшая программа, которая одновременно показывает целый, вещественный и комплексный форматы LibMPU, а затем читает вещественное число через mpu_sscanf():
#include <libmpuio.h>
int
main( void )
{
mpu_int128_t n;
mpu_real128_t r, parsed, re, im;
mpu_complex128_t c;
setlocale( LC_ALL, "" );
__mpu_init();
iatoi( n, (__mpu_char8_t *)"12345", NB_I128 );
ascii_to_real( r, (__mpu_char8_t *)"12345.6789", NB_R128 );
ascii_to_real( re, (__mpu_char8_t *)"1.25", NB_R128 );
ascii_to_real( im, (__mpu_char8_t *)"2.5", NB_R128 );
c_gen_complex( c, re, im, NB_C128 );
mpu_printf( MPU_UCS2( "integer : |%+20Z128d|\n" ), n );
mpu_printf( MPU_UCS2( "real : |%+020.8.4Z128R|\n" ), r );
mpu_printf( MPU_UCS2( "complex : |%+.6.3Z128J|\n" ), c );
if( mpu_sscanf( MPU_UCS2( "1.23456789E+10" ),
MPU_UCS2( "%Z128R" ), parsed ) == 1 )
{
mpu_printf( MPU_UCS2( "parsed : %Z128R\n" ), parsed );
}
__mpu_free_context();
return 0;
}
Фактический вывод:
integer : | +12345| real : |+0001.23456789E+0004| complex : |+1.250000R+000+2.500000J+000| parsed : 1.234568E+10
Этот пример хорошо показывает назначение LibMPUIO: обычная printf‑подобная запись сохраняется даже тогда, когда аргументом становится не машинный int или double, а объект арифметики большой разрядности.
Особенности, о которых важно помнить
Первое правило – %s и %a имеют разные контракты:
%s -> UCS-2, __mpu_char16_t * %a -> current-locale multibyte, __mpu_char8_t *
Для явного преобразования именно UTF‑8 существуют отдельные функции:
mpu_utf8_to_ucs2() mpu_ucs2_to_utf8()
Второе правило – ordinary C numbers и MPU objects используют разные variadic contracts:
int value = 42; mpu_printf( MPU_UCS2( "%d\n" ), value );
и:
mpu_int128_t value; mpu_printf( MPU_UCS2( "%Z128d\n" ), value );
– это принципиально разные вызовы.
Третье правило – размер MPU object является частью format specification. Указатель на arbitrary‑precision object не содержит runtime metadata о разрядности.
Четвёртое правило – не следует путать ordinary %f и MPU %Z<bits>f: первый означает привычный fixed-point floating conversion языка C, а второй является историческим scientific alias MPU %e.
Пятое правило – +, 0, width и alignment применимы не только к обычным C numbers, но и к знаковым MPU integer, real и complex преобразованиям. Поэтому форматы вроде
%+020.8.4Z128R %+040.6.3Z128J
можно использовать для построения строгих диагностических колонок и таблиц.
Наконец, positional arguments в стиле GNU Libc:
%2$d %1$s
в LibMPUIO не реализованы.
Заключение
LibMPUIO решает задачу, которая неизбежно появляется рядом с библиотекой произвольной точности: вычислить большое число недостаточно – его необходимо корректно прочитать, отформатировать, записать в файл, передать через pipe или вывести человеку.
Вместо отдельного набора специальных функций для каждого типа LibMPU библиотека LibMPUIO расширяет хорошо знакомую модель stdio. Программист получает mpu_FILE, printf/scanf‑подобные функции, буферизацию и позиционирование, но одновременно может работать с UCS‑2/UTF‑8 текстовой моделью и передавать в format string точный размер целых, вещественных или комплексных объектов через модификатор z/Z<bits>.
Для практической работы достаточно помнить несколько опорных правил:
| UCS-2 | — | внутри Text API; |
| UTF-8 | — | на границе реального text stream; |
| Raw I/O | — | всегда остаётся byte-oriented; |
| %s | — | UCS-2 string; |
| %a | — | current‑locale multibyte bridge; |
| z/Z<bits> | — | размер и типовой контракт объекта LibMPU; |
| R/E | — | real; |
| J | — | complex. |
После этого большинство функций LibMPUIO читаются почти так же, как знакомый stdio, а специальные места – кодировка и arbitrary‑precision форматы – явно видны непосредственно в исходном коде программы.
Links:
Enjoy.