.TH MPU_PRINTF 3 "September 2026" "libmpuio" "LIBMPUIO Programmer's Manual" .SH NAME mpu_printf, mpu_fprintf, mpu_sprintf, mpu_snprintf, mpu_asprintf, mpu_dprintf, mpu_vprintf, mpu_vfprintf, mpu_vsprintf, mpu_vsnprintf, mpu_vasprintf, mpu_vdprintf, mpu_printf_unlocked, mpu_fprintf_unlocked, mpu_vprintf_unlocked, mpu_vfprintf_unlocked \- formatted UCS-2 output, including arbitrary precision libmpu numbers .SH SYNOPSIS .nf #include int mpu_printf( const __mpu_char16_t *format, ... ); int mpu_fprintf( mpu_FILE *stream, const __mpu_char16_t *format, ... ); int mpu_sprintf( __mpu_char16_t *string, const __mpu_char16_t *format, ... ); int mpu_snprintf( __mpu_char16_t *string, size_t size, const __mpu_char16_t *format, ... ); int mpu_asprintf( __mpu_char16_t **string, const __mpu_char16_t *format, ... ); int mpu_dprintf( int fd, const __mpu_char16_t *format, ... ); int mpu_vprintf( const __mpu_char16_t *format, va_list args ); int mpu_vfprintf( mpu_FILE *stream, const __mpu_char16_t *format, va_list args ); int mpu_vsprintf( __mpu_char16_t *string, const __mpu_char16_t *format, va_list args ); int mpu_vsnprintf( __mpu_char16_t *string, size_t size, const __mpu_char16_t *format, va_list args ); int mpu_vasprintf( __mpu_char16_t **string, const __mpu_char16_t *format, va_list args ); int mpu_vdprintf( int fd, const __mpu_char16_t *format, va_list args ); int mpu_printf_unlocked( const __mpu_char16_t *format, ... ); int mpu_fprintf_unlocked( mpu_FILE *stream, const __mpu_char16_t *format, ... ); int mpu_vprintf_unlocked( const __mpu_char16_t *format, va_list args ); int mpu_vfprintf_unlocked( mpu_FILE *stream, const __mpu_char16_t *format, va_list args ); .fi .SH DESCRIPTION The .B mpu_printf() family produces formatted text from a UCS-2 format string. .B mpu_printf() and .B mpu_vprintf() write to .BR mpu_stdout . .B mpu_fprintf() and .B mpu_vfprintf() write to the specified .BR mpu_FILE . .B mpu_sprintf() and .B mpu_vsprintf() write to an unbounded UCS-2 memory string supplied by the caller. .B mpu_snprintf() and .B mpu_vsnprintf() write at most .I size UCS-2 code units, including the terminating zero. .PP For real file streams the UCS-2 text produced by the formatter is encoded as UTF-8 by the stream layer. Memory/string destinations remain UCS-2. .PP .B mpu_asprintf() and .B mpu_vasprintf() allocate a sufficiently large NUL-terminated UCS-2 result with .BR malloc (3). On success they store that pointer through .I string; the caller releases it with .BR free (3). If the operation fails, .I *string is set to NULL. .PP .B mpu_dprintf() and .B mpu_vdprintf() write formatted text to an existing POSIX file descriptor. The descriptor is not closed and ownership remains with the caller. Text is encoded to UTF-8 using the same external representation rules as an ordinary descriptor-backed LIBMPUIO file stream. .PP The ordinary conversion syntax follows printf conventions. LIBMPUIO extends that syntax with arbitrary-precision integer, real and complex conversions for libmpu objects. .SH FORMAT OF THE FORMAT STRING Ordinary characters in .I format are copied unchanged. A conversion specification begins with .B % and has the form .PP .nf %[flags][width][.precision][.expdigits][length-or-Zbits]conversion .fi .PP The .B .expdigits field is a LIBMPUIO extension used by MPU real and complex conversions. It is not part of ordinary C printf syntax. .PP A literal percent sign is written with .BR %% . .SH FLAGS The following flags are recognized. .TP .B - Left-justify the converted value within the field. The .B 0 flag is ignored when .B - is present. .TP .B + Always print a sign for signed numeric conversions. .TP .B " " Prefix a positive signed numeric value with a space. The .B + flag takes precedence. .TP .B # Select alternate form. For octal output this ensures a leading zero. For hexadecimal output a nonzero value receives .B 0x or .BR 0X . For binary .B b/B a nonzero value receives .B 0b or .BR 0B . For ordinary floating conversions the flag has the usual printf alternate-form meaning. For MPU general format it also prevents removal of insignificant trailing fractional zeroes and the decimal point. .TP .B 0 Pad numeric fields with zeroes instead of spaces when applicable. For integer conversions the flag is ignored when an explicit precision is present. .SH FIELD WIDTH A decimal number specifies the minimum field width. The converted value is padded if necessary, but is never truncated merely to satisfy the width. .PP A width of .B * is taken from the next .B int argument. A negative width is treated as a positive width together with the .B - flag. .SH PRECISION A precision begins with a dot. It can be a decimal integer or .BR * . A negative precision supplied through .B * is treated as if no precision had been specified. .PP For .B d, i, u, o, x, X, b and .B B, precision is the minimum number of digits. An integer value zero printed with precision zero produces no digits, subject to alternate-form rules. .PP For .B s, precision is the maximum number of UCS-2 characters written from the string. Precision has no effect on .BR c . .PP For ordinary floating conversions the precision has the usual printf meaning. For MPU floating conversions see .B MPU REAL CONVERSIONS below. .SH LENGTH MODIFIERS For ordinary integer conversions the following length modifiers are supported. .TP .B hh The argument is converted as .B signed char or .BR unsigned char . .TP .B h The argument is converted as .B short or .BR unsigned short . .TP .B l The argument is .B long or .BR unsigned long . For ordinary floating output, as in C printf, .B l does not change the promoted .B double argument. .TP .B ll The argument is .B long long or .BR unsigned long long . .TP .B j The argument is .B intmax_t or .BR uintmax_t . For .B %n the argument is an .B intmax_t * when .B j is used. .TP .B t The argument is .B ptrdiff_t or its corresponding unsigned interpretation. .TP .B L For ordinary floating conversions the argument is .BR "long double" . .PP The standard C .B z length modifier is intentionally not implemented. In LIBMPUIO both .B z and .B Z introduce the LibMPU size modifier described below. .PP The letter .B j also names the historical MPU complex conversion. It is interpreted as the standard .B intmax_t length modifier when followed by an ordinary integer conversion or .BR n ; otherwise it is an MPU complex conversion. .SH ORDINARY CONVERSIONS .TP .B d, i Print a signed decimal integer. .TP .B u Print an unsigned decimal integer. .TP .B o Print an unsigned integer in octal. .TP .B x, X Print an unsigned integer in hexadecimal. Digits and the alternate-form prefix use lower case for .B x and upper case for .BR X . .TP .B b, B LIBMPUIO extension: print an unsigned integer in binary. With .B #, a nonzero value receives .B 0b or .BR 0B . .TP .B c Write one UCS-2 character. Because LIBMPUIO character I/O is UCS-2, the argument is an .B int whose value is converted to .BR __mpu_char16_t . This is not a libc multibyte/wchar_t conversion. .TP .B s Write a NUL-terminated UCS-2 string of type .BR "const __mpu_char16_t *" . A precision limits the number of UCS-2 characters written. A NULL pointer is rendered as .BR (null) . .TP .B a LIBMPUIO extension: write a NUL-terminated current-locale multibyte string of type .BR "const __mpu_char8_t *" . The byte string is decoded according to the active .B LC_CTYPE locale and converted to strict UCS-2 before it enters the LIBMPUIO stream. Field width and precision are measured in converted UCS-2 characters. A NULL pointer is rendered as .BR (null) . An invalid multibyte sequence, a surrogate, or a character outside the UCS-2 model causes the conversion to fail with .BR EILSEQ . Length modifiers and the MPU .B z/Z modifier do not apply to this conversion. .TP .B p Write a pointer value in hexadecimal alternate form. The argument type is .BR "void *" . .TP .B n Write the number of UCS-2 characters produced so far through a pointer argument. No characters are produced by this conversion. The pointer type is selected by the length modifier: no modifier gives .B int *; .B hh, h, l, ll, j, and .B t select the corresponding integer pointer types. .TP .B f, F Print an ordinary .B double (or .B long double with .B L) in fixed-point notation. .TP .B e, E Print an ordinary floating value in scientific notation. .TP .B g, G Print an ordinary floating value in general format. .PP Ordinary .B f/F/e/E/g/G output is delegated to the host libc formatting rules and therefore follows the active .B LC_NUMERIC radix character. LIBMPUIO decodes the resulting libc multibyte field to strict UCS-2 before it enters an LIBMPUIO stream; invalid sequences, surrogate output, or characters outside the UCS-2 model are rejected with .BR EILSEQ . The corresponding ordinary scanf conversions use the same locale radix convention. The MPU .B z/Z formatted-number grammar is separate from this ordinary C locale behavior. .SH MPU SIZE MODIFIER The LibMPU arbitrary-precision size modifier is .B z or .BR Z . The lower-case and upper-case forms are equivalent. The standard C .B z length modifier for .B size_t is intentionally not part of the LIBMPUIO format language. .PP The decimal .I bits field is not an arbitrary run-time number. It must name one of the MPU sizes compiled into the current LIBMPUIO grammar: .PP .nf 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536 .fi .PP Only entries not greater than .B MPU_REAL_IO_LIMIT from .B exist in a particular build. For example, when .B MPU_REAL_IO_LIMIT is 16384, the modifiers .B z32768, .B Z32768, .B z65536 and .B Z65536 do not exist in that LIBMPUIO format grammar and cause a format error. .PP If no decimal digits follow .B z or .BR Z , 128 bits are assumed. Therefore .B %zu is an MPU 128-bit unsigned-integer conversion, exactly equivalent in size to .BR %z128u ; it is .B not a .B size_t conversion. Likewise .B %zR, .B %ZR, .B %zJ and .B %ZJ use the default 128-bit MPU size. .PP The size modifier is parsed before the conversion letter. Once the size is accepted, the conversion letter selects the numeric class: integer conversions use a libmpu integer object, real conversions use a libmpu real object, and .B j/J use a libmpu complex object. Integer conversions accept MPU sizes from 8 bits; real and complex conversions require at least 32 bits. .PP .B MPU_MATH_FN_LIMIT does not define the formatted-I/O grammar. It limits transcendental/math routines only. The set of available .B z/Z modifiers is controlled by .BR MPU_REAL_IO_LIMIT . .SH ARGUMENT TYPE MODEL Ordinary C conversions and MPU conversions use deliberately different argument conventions. .PP For an ordinary conversion the conversion letter and standard length modifier describe the C type that .BR va_arg () must fetch. The argument is passed by value, subject to the usual default argument promotions. For example: .PP .nf char k = 7; mpu_printf( MPU_UCS2("k = %d\\n"), k ); .fi .PP Here .B k is promoted to .B int before it enters the variadic argument list, and .B %d selects an .B int value. The formatter does not discover the argument type at run time; the format string tells it which C type to fetch. .PP An MPU conversion selected by .B z or .B Z has a different contract. Its argument is a pointer to the storage of an MPU object, and the size modifier tells the formatter exactly how many bits that object contains. For the array typedefs used by libmpu, an object name in a function-call argument normally undergoes the standard array-to-pointer conversion, so the natural spelling is: .PP .nf mpu_int128_t c; mpu_real128_t r; mpu_printf( MPU_UCS2("c = %z128u\\n"), c ); mpu_printf( MPU_UCS2("r = %z128g\\n"), r ); .fi .PP The pointer itself carries no object-size metadata. Consequently a variadic formatter cannot infer whether a pointer designates, for example, a 32-bit, 128-bit, or 65536-bit MPU object. The explicit .B z/Z field is therefore part of the type contract of an MPU conversion rather than redundant decoration. .PP It is technically possible to point an MPU conversion at storage belonging to an ordinary C object when its exact representation and size are deliberately known, for example: .PP .nf char k = 7; mpu_printf( MPU_UCS2("k = %z8d\\n"), &k ); .fi .PP This is a low-level interpretation of the byte at .B &k as an 8-bit MPU integer object; it is .B not the ordinary C .B char printing convention and should not be used as a portable substitute for .BR %d . For multi-byte C objects such use can additionally depend on representation, byte order, and compatibility with libmpu storage. Ordinary C objects should normally use ordinary C conversions; MPU objects should use .BR z/Z . .PP Changing libmpu array typedefs to structure typedefs would not make the variadic function self-describing. A C .B va_list does not carry general run-time type or size metadata, and .B va_arg() still requires the expected type to be known from the format contract. .SH MPU INTEGER CONVERSIONS With .BR z/Z , .B d and .B i format a signed arbitrary-precision integer. The conversions .B u, o, x, X, b and .B B format an unsigned arbitrary-precision integer. The argument is a pointer to a libmpu integer object whose storage size matches the selected bit size. .PP Examples: .PP .nf %Z128d %#Z256x %#Z1024B %08.3Z128d .fi .PP The formatter removes libmpu's internal radix prefix before applying printf alternate-form rules itself. Thus .B # has the same externally visible meaning for ordinary and MPU integer output. .SH MPU REAL CONVERSIONS The native MPU real conversions are .B r and .BR R . If no explicit .B z/Z modifier is present they use 128 bits. .PP The conversion letter chooses case, but the exponent marker emitted for a real number is .B e for .B %r and .B E for .BR %R . The letters .B r/R are conversion specifiers, not the printed real exponent delimiter. .PP Examples: .PP .nf %Z128R %.28Z128R %.28.4Z128R .fi .PP The first precision controls mantissa digits. The optional second .B .expdigits field specifies the minimum exponent width. It may also be supplied with .BR * . For example: .PP .nf %.4.3Z128e -> 1.2346e+004 .fi .PP The MPU forms .B z/Ze, .B z/ZE, .B z/Zf, .B z/ZF, .B z/Zg and .B z/ZG are also supported. .PP All of these real conversions fetch a pointer to a libmpu real object of the selected size. In particular, both .B %Z128R and .B %Z128E expect an .B mpu_real128_t object. Complex output is separate: .B %Z128J expects an .B mpu_complex128_t object. Thus the type mapping is: .PP .nf %Z128R -> mpu_real128_t %Z128E -> mpu_real128_t %Z128J -> mpu_complex128_t .fi .PP For historical CODE.LIB compatibility, .B MPU f/F are scientific aliases of e/E; they are not fixed-point conversions. This differs deliberately from ordinary C .BR %f/%F . .PP For MPU .B g/G, a missing precision defaults to 6 and a precision of zero is treated as one significant digit. Scientific notation is selected when the decimal exponent is less than -4 or greater than or equal to the precision. Otherwise fixed style is used. Trailing fractional zeroes and the decimal point are removed unless .B # is specified. .PP Lowercase .B %a is the LIBMPUIO current-locale multibyte-string conversion. .SH MPU COMPLEX CONVERSIONS The native MPU complex conversions are .B j and .BR J . If no explicit MPU size is supplied they default to 128 bits. The argument is a pointer to a libmpu complex object for the selected size. .PP The real and imaginary components are concatenated using the historical MPU complex representation. For example: .PP .nf %.20.4Z128J -> 1.25000000000000000000R+0000-2.50000000000000000000J+0000 .fi .PP For .B %j the component exponent markers are .B r and .BR j . For .B %J they are .B R and .BR J . This historical complex syntax is intentional. It differs from real-only .B %r/%R, whose printed exponent marker is .BR e/E . .PP When the .B 0 flag is used with a field wider than the formatted complex value, the free field width is divided equally between the real and imaginary components. Zeroes are inserted after the sign of each component. If the free width is odd, the one remaining character is emitted as ordinary leading space padding. For example, for a value whose components are 1.25 and 2.5: .PP .nf %+040.6.3Z128J -> +0000001.250000R+000+0000002.500000J+000 %+039.6.3Z128J -> +000001.250000R+000+000002.500000J+000 .fi .SH STRING OUTPUT AND TRUNCATION .B mpu_sprintf() and .B mpu_vsprintf() do not know the destination capacity. The caller must provide enough UCS-2 storage for the complete result plus a terminating zero. .PP .B mpu_snprintf() and .B mpu_vsnprintf() write at most .I size UCS-2 code units including the terminating zero. If .I size is zero, no destination data is written. The return value is the number of UCS-2 characters that would have been produced if sufficient space had been available, excluding the terminating zero. .SH LOCKING The normal stream variants lock the .B mpu_FILE while formatting. The .B *_unlocked variants do not acquire the stream mutex. They are intended for code that has already serialized access, commonly with .BR mpu_flockfile (3): .PP .nf mpu_flockfile( fp ); mpu_fprintf_unlocked( fp, fmt, ... ); mpu_fflush_unlocked( fp ); mpu_funlockfile( fp ); .fi .PP The string-output functions use private memory streams and have no public unlocked variants. .SH SNPRINTF SIZING AND TRUNCATION For .B mpu_snprintf() and .B mpu_vsnprintf(), the .I size argument is the capacity of the UCS-2 destination array, including the final NUL character. If .I size is zero, no destination characters are stored and .I string may be NULL. The functions still parse the complete format and return the number of UCS-2 characters that would have been produced, excluding the final NUL. This permits the usual two-pass allocation pattern. .PP When the output is truncated, the return value and any .B %n conversion use the logical character count, not the number of characters that fit in the destination array. .PP If the logical formatted-output count cannot be represented by .B int, the operation fails with a negative return value and .B errno set to .B EOVERFLOW. .SH RETURN VALUE On success these functions return the number of UCS-2 characters produced, excluding the terminating zero used by string destinations. For .BR mpu_asprintf () and .BR mpu_vasprintf (), this is the number of allocated UCS-2 code units before the terminating NUL. For .BR mpu_dprintf () and .BR mpu_vdprintf (), the return count is still in UCS-2 characters, not in the number of UTF-8 bytes written to the descriptor. A negative value indicates failure. .PP .B %n does not contribute an argument assignment to the return value; it merely stores the count accumulated at that point. .SH ERRORS Errors from the underlying stream and memory allocation are reported through a negative return value and, where applicable, .BR errno . .B mpu_asprintf() and .B mpu_vasprintf() report allocation failure as .BR ENOMEM ; a NULL destination pointer is rejected with .BR EINVAL . .B mpu_dprintf() and .B mpu_vdprintf() can report descriptor and write errors such as .BR EBADF . An invalid configured MPU size causes failure with .BR EINVAL . The strict UCS-2 file boundary can report .B EILSEQ when a character cannot be represented according to LIBMPUIO's UCS-2/UTF-8 rules. .SH EXAMPLES A UCS-2 string and ordinary values: .PP .nf __mpu_char16_t fmt[] = MPU_UCS2("name=%s value=%08x"); __mpu_char16_t name[] = MPU_UCS2("mpu"); mpu_printf( fmt, name, 0x1234U ); .fi .PP A current-locale multibyte string: .PP .nf __mpu_char8_t name8[] = "Andrey"; mpu_printf( MPU_UCS2("name=%a\n"), name8 ); .fi .PP An arbitrary-precision integer: .PP .nf __mpu_char16_t fmt[] = MPU_UCS2("%#Z256x"); mpu_printf( fmt, &integer256 ); .fi .PP A 128-bit real with 28 mantissa digits and four exponent digits: .PP .nf __mpu_char16_t fmt[] = MPU_UCS2("%.28.4Z128E"); mpu_fprintf( fp, fmt, &real128 ); .fi .SH UCS-2 STRING LITERALS The public macro .B MPU_UCS2() forms a native 16-bit string literal using the .B u"..." source-language prefix. For example: .PP .nf mpu_printf( MPU_UCS2( "value=%d" ), value ); .fi .PP Unlike .B L"...", which uses the platform .B wchar_t type, .B MPU_UCS2() is intended for LIBMPUIO interfaces whose text arguments use the LIBMPU .B __mpu_char16_t type. .PP The public header enables this facility when the translation mode provides 16-bit .B u"..." string literals: C11 or later, C++11 or later, or a GNU C mode that supports the extension. A build configuration may also define .B MPU_HAVE_UCS2_STRING_LITERALS before including .B to state explicitly whether the facility is available. If it is unavailable, the public header reports a compile-time error. .PP The literal prefix is a source-language facility, not a runtime encoding switch. LIBMPUIO still applies strict UCS-2 rules and rejects surrogate code units with .B EILSEQ. .SH NOTES LIBMPUIO uses 16-bit UCS-2 internally rather than the platform .B wchar_t type. Consequently .B %c and .B %s operate directly on UCS-2 characters and strings and do not have libc .B %lc/%ls multibyte semantics. The LIBMPUIO .B %a conversion is the explicit current-locale multibyte-string interface; its argument type is .BR "const __mpu_char8_t *" . .PP Strict UCS-2 cannot represent Unicode scalar values above U+FFFF. Such input is not represented as surrogate pairs by the text layer. .SH SEE ALSO .BR mpu_scanf (3), .BR mpu_flockfile (3), .BR mpu_unlocked (3), .BR mpu_fopen (3), .BR libmpuio (3) .SH LOCALE For ordinary floating-point conversions, the decimal-point character follows the active C locale in the same way as the system .BR printf (3) family. Thus a locale whose .B LC_NUMERIC uses a comma may produce, for example, .B 1,50 for .BR %.2f . The LIBMPUIO regression suite selects the C locale after initializing libmpu so that its expected strings are reproducible. .SH FORMAT PARSER OVERFLOW The format parser detects decimal accumulation overflow in field width, precision, the LIBMPU exponent-width extension, and z/Z size fields. It also rejects INT_MIN supplied as a negative * field width, since that value cannot be represented as a positive int width. These cases fail with a negative return value and set errno to EOVERFLOW. .SH LOCKED VERSUS UNLOCKED FORMATTED OUTPUT .BR mpu_fprintf_unlocked (), .BR mpu_vfprintf_unlocked (), .BR mpu_printf_unlocked (), and .BR mpu_vprintf_unlocked () use the exact same formatter as the ordinary stream forms but omit implicit stream locking. Format parsing, MPU z/Z handling, locale behavior, %n, logical output counts, truncation rules, and errors are unchanged. .PP The sprintf/snprintf families write caller-owned UCS-2 memory rather than an mpu_FILE stream and therefore have no stream-unlocked variants.