Новости и статьи

Среда разработки встроенных систем или дистрибутивов на основе GNU/Linux. Здесь вы можете найти новости проекта, статьи и заметки, касающиеся разработки дистрибутива Radix cross Linux.

LibMPUIO (Multiple Precision Input/Output)

6 сентября 2026 г.

LibMPUIO – библиотека ввода‑вывода для программ, использующих LibMPU и числа большой разрядности. По устройству интерфейса она близка к стандартному stdio, в библиотеке есть собственный тип потока mpu_FILE, стандартные потоки, файловый ввод‑вывод, буферизация, позиционирование, printf/scanf, memory streams, popen, cookie streams и _unlocked‑варианты функций.

libmpuio

Главная особенность 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:

Рис.1. Модель представления текста.

Для реального потока преобразование выполняется автоматически:

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.