Как напечатать номер __uint128_t с помощью GCC?
Есть PRIu128
который ведет себя подобно PRIu64
от <inttypes.h>
:
printf("%" PRIu64 "\n", some_uint64_value);
Или преобразование вручную цифра за цифрой:
int print_uint128(uint128_t n) {
if (n == 0) return printf("0\n");
char str[40] = {0}; // log10(1 << 128) + '\0'
char *s = str + sizeof(str) - 1; // start at the end
while (n != 0) {
if (s == str) return -1; // never happens
*--s = "0123456789"[n % 10]; // save last digit
n /= 10; // drop it
}
return printf("%s\n", s);
}
это единственный вариант?
Обратите внимание, что uint128_t
мой собственный typedef для __uint128_t
,
16 ответов
Нет, в библиотеке нет поддержки для печати этих типов. Они даже не являются расширенными целочисленными типами в смысле стандарта C.
Ваша идея начать печать со спины хороша, но вы можете использовать гораздо большие куски. В некоторых тестах на P99 у меня есть такая функция, которая использует
uint64_t const d19 = UINT64_C(10000000000000000000);
как наибольшая сила 10, которая вписывается в uint64_t
,
Как десятичные, эти большие числа становятся нечитаемыми очень скоро, поэтому другой, более простой вариант - напечатать их в шестнадцатеричном виде. Тогда вы можете сделать что-то вроде
uint64_t low = (uint64_t)x;
// This is UINT64_MAX, the largest number in 64 bit
// so the longest string that the lower half can occupy
char buf[] = { "18446744073709551615" };
sprintf(buf, "%" PRIX64, low);
чтобы получить нижнюю половину, а затем в основном то же самое с
uint64_t high = (x >> 64);
для верхней половины.
Руководство GCC 4.7.1 гласит:
6,8 128-битных целых
Как расширение целочисленный скалярный тип
__int128
поддерживается для целей, имеющих целочисленный режим, достаточно широкий, чтобы содержать 128-бит. Просто пиши__int128
для 128-разрядного целого числа со знаком илиunsigned __int128
для 128-битного целого без знака. В GCC нет поддержки для выражения целочисленной константы типа__int128
для целей, имеющихlong long
целое число с шириной менее [sic] 128 бит.
Интересно, хотя это не упоминает __uint128_t
этот тип принят, даже если установлены строгие предупреждения:
#include <stdio.h>
int main(void)
{
__uint128_t u128 = 12345678900987654321;
printf("%llx\n", (unsigned long long)(u128 & 0xFFFFFFFFFFFFFFFF));
return(0);
}
Компиляция:
$ gcc -O3 -g -std=c99 -Wall -Wextra -pedantic xxx.c -o xxx
xxx.c: In function ‘main’:
xxx.c:6:24: warning: integer constant is so large that it is unsigned [enabled by default]
$
(Это с домашним компилятором GCC 4.7.1 на Mac OS X 10.7.4.)
Измените константу на 0x12345678900987654321
и компилятор говорит:
xxx.c: In function ‘main’:
xxx.c:6:24: warning: integer constant is too large for its type [enabled by default]
Так что манипулировать этими существами нелегко. Выходные данные с десятичной и шестнадцатеричной константами:
ab54a98cdc6770b1
5678900987654321
Для печати в десятичном формате лучше всего посмотреть, превышает ли это значение значение UINT64_MAX; если это так, то вы делите на наибольшую степень 10, меньшую, чем UINT64_MAX, печатаете это число (и вам может потребоваться повторить процесс второй раз), а затем выводите остаток по модулю наибольшей степени 10, которая меньше, чем UINT64_MAX, не забывая дополнять ведущими нулями.
Это приводит к чему-то вроде:
#include <stdio.h>
#include <inttypes.h>
/*
** Using documented GCC type unsigned __int128 instead of undocumented
** obsolescent typedef name __uint128_t. Works with GCC 4.7.1 but not
** GCC 4.1.2 (but __uint128_t works with GCC 4.1.2) on Mac OS X 10.7.4.
*/
typedef unsigned __int128 uint128_t;
/* UINT64_MAX 18446744073709551615ULL */
#define P10_UINT64 10000000000000000000ULL /* 19 zeroes */
#define E10_UINT64 19
#define STRINGIZER(x) # x
#define TO_STRING(x) STRINGIZER(x)
static int print_u128_u(uint128_t u128)
{
int rc;
if (u128 > UINT64_MAX)
{
uint128_t leading = u128 / P10_UINT64;
uint64_t trailing = u128 % P10_UINT64;
rc = print_u128_u(leading);
rc += printf("%." TO_STRING(E10_UINT64) PRIu64, trailing);
}
else
{
uint64_t u64 = u128;
rc = printf("%" PRIu64, u64);
}
return rc;
}
int main(void)
{
uint128_t u128a = ((uint128_t)UINT64_MAX + 1) * 0x1234567890ABCDEFULL +
0xFEDCBA9876543210ULL;
uint128_t u128b = ((uint128_t)UINT64_MAX + 1) * 0xF234567890ABCDEFULL +
0x1EDCBA987654320FULL;
int ndigits = print_u128_u(u128a);
printf("\n%d digits\n", ndigits);
ndigits = print_u128_u(u128b);
printf("\n%d digits\n", ndigits);
return(0);
}
Выход из этого:
24197857200151252746022455506638221840
38 digits
321944928255972408260334335944939549199
39 digits
Мы можем проверить, используя bc
:
$ bc
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
ibase = 16
1234567890ABCDEFFEDCBA9876543210
24197857200151252746022455506638221840
F234567890ABCDEF1EDCBA987654320F
321944928255972408260334335944939549199
quit
$
Понятно, что для hex процесс проще; Вы можете сдвигать, маскировать и печатать всего за две операции. Для восьмеричного числа, поскольку 64 не является кратным 3, вы должны выполнить аналогичные шаги для десятичной операции.
print_u128_u()
интерфейс не идеален, но он по крайней мере возвращает количество напечатанных символов, так же, как printf()
делает. Адаптация кода для форматирования результата в строковый буфер - не совсем тривиальное упражнение в программировании, но не очень сложная.
У меня нет встроенного решения, но деление / модуль стоит дорого. Вы можете конвертировать двоичные в десятичные с помощью только сдвигов.
static char *qtoa(uint128_t n) {
static char buf[40];
unsigned int i, j, m = 39;
memset(buf, 0, 40);
for (i = 128; i-- > 0;) {
int carry = !!(n & ((uint128_t)1 << i));
for (j = 39; j-- > m + 1 || carry;) {
int d = 2 * buf[j] + carry;
carry = d > 9;
buf[j] = carry ? d - 10 : d;
}
m = j;
}
for (i = 0; i < 38; i++) {
if (buf[i]) {
break;
}
}
for (j = i; j < 39; j++) {
buf[j] += '0';
}
return buf + i;
}
(Но, очевидно, 128-битное деление / модуль не так дороги, как я думал. На Phenom 9600 с GCC 4.7 и Clang 3.1 на -O2
кажется, это работает в 2–3 раза медленнее, чем метод OP.)
Основываясь на ответе Себастьяна, это для подписанного int128 в g++, не является потокобезопасным.
// g++ -Wall fact128.c && a.exe
// 35! overflows 128bits
#include <stdio.h>
char * sprintf_int128( __int128_t n ) {
static char str[41] = { 0 }; // sign + log10(2**128) + '\0'
char *s = str + sizeof( str ) - 1; // start at the end
bool neg = n < 0;
if( neg )
n = -n;
do {
*--s = "0123456789"[n % 10]; // save last digit
n /= 10; // drop it
} while ( n );
if( neg )
*--s = '-';
return s;
}
__int128_t factorial( __int128_t i ) {
return i < 2 ? i : i * factorial( i - 1 );
}
int main( ) {
for( int i = 0; i < 35; i++ )
printf( "fact(%d)=%s\n", i, sprintf_int128( factorial( i ) ) );
return 0;
}
Я думаю твой print_uint128
Функция ужасно сложна.
Разве это не проще написать и запустить?
void print_uint128(uint128_t n)
{
if (n == 0) {
return;
}
print_uint128(n/10);
putchar(n%10+0x30);
}
Вы можете использовать этот простой макрос:
typedef __int128_t int128 ;
typedef __uint128_t uint128 ;
uint128 x = (uint128) 123;
printf("__int128 max %016"PRIx64"%016"PRIx64"\n",(uint64)(x>>64),(uint64)x);
Как напечатать номер __uint128_t с помощью GCC?
Есть ли PRIu128, который ведет себя подобно PRIu64 из:
Нет. Вместо того, чтобы печатать в десятичном виде, печатать в строку.
Необходимого размера строкового буфера достаточно, чтобы выполнить задание в соответствии со значением x
,
typedef signed __int128 int128_t;
typedef unsigned __int128 uint128_t;
// Return pointer to the end
static char *uint128toa_helper(char *dest, uint128_t x) {
if (x >= 10) {
dest = uint128toa_helper(dest, x / 10);
}
*dest = (char) (x % 10 + '0');
return ++dest;
}
char *int128toa(char *dest, int128_t x) {
if (x < 0) {
*dest = '-';
*uint128toa_helper(dest + 1, (uint128_t) (-1 - x) + 1) = '\0';
} else {
*uint128toa_helper(dest, (uint128_t) x) = '\0';
}
return dest;
}
char *uint128toa(char *dest, uint128_t x) {
*uint128toa_helper(dest, x) = '\0';
return dest;
}
Тестовое задание. В худшем случае размер буфера: 41.
int main(void) {
char buf[41];
puts("1234567890123456789012345678901234567890");
puts(uint128toa(buf, 0));
puts(uint128toa(buf, 1));
puts(uint128toa(buf, (uint128_t) -1));
int128_t mx = ((uint128_t) -1) / 2;
puts(int128toa(buf, -mx - 1));
puts(int128toa(buf, -mx));
puts(int128toa(buf, -1));
puts(int128toa(buf, 0));
puts(int128toa(buf, 1));
puts(int128toa(buf, mx));
return 0;
}
Выход
1234567890123456789012345678901234567890
0
1
340282366920938463463374607431768211455
-170141183460469231731687303715884105728
-170141183460469231731687303715884105727
-1
0
1
170141183460469231731687303715884105727
Я хотел печатать беззнаковые 64/128-битные числа в десятичной форме и не хотел изобретать велосипед. Итак, «pu128()» имеет 3 случая: <10^19, <10^38, в противном случае. Возможно, не самый быстрый, но должен быть портативным. Определяет макросы UINT128_MAX, а также UINT128_C.
$ gcc -Wall -Wextra -pedantic lu.c
$ ./a.out
0
10000000000000000000
18446744073709551615
0
10000000000000000000
18446744073709551615
100000000000000000000000000000000000000
340282366920938463463374607431768211455
$
$ cat lu.c
#include <stdio.h>
#include <inttypes.h>
#define UINT128_C(u) ((__uint128_t)u)
void pu64(__uint64_t u) { printf("%" PRIu64, u); }
void pu640(__uint64_t u) { printf("%019" PRIu64, u); }
#define D19_ UINT64_C(10000000000000000000)
const __uint128_t d19_ = D19_;
const __uint128_t d38_ = UINT128_C(D19_)*D19_;
const __uint128_t UINT128_MAX = UINT128_C(UINT64_MAX)<<64 | UINT64_MAX;
void pu128(__uint128_t u)
{
if (u < d19_) pu64(u);
else if (u < d38_) { pu64(u/d19_); pu640(u%d19_); }
else { pu64(u/d38_); u%=d38_; pu640(u/d19_); pu640(u%d19_); }
}
int main()
{
pu64(0); puts("");
pu64(d19_); puts("");
pu64(UINT64_MAX); puts("");
pu128(0); puts("");
pu128(d19_); puts("");
pu128(UINT64_MAX); puts("");
pu128(d38_); puts("");
pu128(UINT128_MAX); puts("");
}
$
Отрабатывая ответ Абеленки выше, я придумал это.
void uint128_to_str_iter(uint128_t n, char *out,int firstiter){
static int offset=0;
if (firstiter){
offset=0;
}
if (n == 0) {
return;
}
uint128_to_str_iter(n/10,out,0);
out[offset++]=n%10+0x30;
}
char* uint128_to_str(uint128_t n){
char *out=calloc(sizeof(char),40);
uint128_to_str_iter(n, out, 1);
return out;
}
Который, кажется, работает как задумано.
В моем предыдущем ответе я показал, как печатал 128-битные числа на основе printf().
Я реализовал 256-битный беззнаковый целочисленный тип uint256_t как:
typedef __uint128_t uint256_t[2];
Я выполнил необходимые операции, например, «sqr ()», принимая __uint128_t в качестве аргумента и вычисляя uint256_t в качестве результата.
У меня был шестнадцатеричный вывод для uint256_t, а теперь мне нужен десятичный вывод. Но в настоящее время в моем uint256_t есть только «mod_256 ()», но нет «div ()», поэтому «n / =10» во многих ответах не было вариантом. Я нашел (медленное) решение, которое работает, и, поскольку я использую только распечатки вне временных секций, это приемлемо. Код можно найти в этой сущности (включая сведения о команде компиляции):
https://gist.github.com/Hermann-SW/83c8ab9e10a0bb64d770af543ed08445
Если вы запускаете sqr.cpp с аргументом, он просто выводит UINT256_MAX и выходит:
if (argc>1) { pu256(UINT256_MAX); puts(""); return 0; }
$ ./sqr 1
115792089237316195423570985008687907853269984665640564039457584007913129639935
$
Сложной частью был рекурсивный вызов, чтобы подняться до максимально используемой цифры, вычесть 1-ю цифру и вывести ее. Все остальное сделает рекурсия. Функция «pu256()» использовала быстрое умножение на 10 «mul10 ()»:
...
void mul10(uint256_t d, uint256_t x)
{
uint256_t t = { x[0], x[1] };
shl_256(t, 2);
add_256(d, x, t);
shl_256(d, 1);
}
const uint256_t UINT256_MAX_10th = UINT256( UINT128(0x1999999999999999, 0x9999999999999999), UINT128(0x9999999999999999, 0x999999999999999A) );
void pu256_(uint256_t v, uint256_t t, const uint256_t o)
{
if (!lt_256(v, t) && le_256(o, UINT256_MAX_10th))
{
uint256_t nt, no = { t[0], t[1] };
mul10(nt, t);
pu256_(v, nt, no);
}
char d = '0';
while (le_256(o, v))
{
sub_256(v, v, o);
++d;
}
putchar(d);
}
void pu256(const uint256_t u)
{
if ((u[1]==0) && (u[0]==0)) putchar('0');
else
{
uint256_t v = { u[0], u[1] }, t = UINT256( 0, 10 ), o = UINT256( 0, 1 );
pu256_(v, t, o);
}
}
...
Как уже говорилось, этот подход имеет смысл только для операции деления с отсутствием целочисленного типа.
Вот модифицированная версия ответа Леффлера, который поддерживает от 0 до UINT128_MAX
/* UINT64_MAX 18446744073709551615ULL */
#define P10_UINT64 10000000000000000000ULL /* 19 zeroes */
#define E10_UINT64 19
#define STRINGIZER(x) # x
#define TO_STRING(x) STRINGIZER(x)
int print_uint128_decimal(__uint128_t big) {
size_t rc = 0;
size_t i = 0;
if (big >> 64) {
char buf[40];
while (big / P10_UINT64) {
rc += sprintf(buf + E10_UINT64 * i, "%." TO_STRING(E10_UINT64) PRIu64, (uint64_t)(big % P10_UINT64));
++i;
big /= P10_UINT64;
}
rc += printf("%" PRIu64, (uint64_t)big);
while (i--) {
fwrite(buf + E10_UINT64 * i, sizeof(char), E10_UINT64, stdout);
}
} else {
rc += printf("%" PRIu64, (uint64_t)big);
}
return rc;
}
И попробуйте это:
print_uint128_decimal(-1); // Assuming -1's complement being 0xFFFFF...
C++ вариант. Вы можете использовать его как шаблон для получения специализированной C-версии функции:
template< typename I >
void print_uint(I value)
{
static_assert(std::is_unsigned< I >::value, "!");
if (value == 0) {
putchar_unlocked('0');
return;
}
I rev = value;
I count = 0;
while ((rev % 10) == 0) {
++count;
rev /= 10;
}
rev = 0;
while (value != 0) {
rev = (rev * 10) + (value % 10);
value /= 10;
}
while (rev != 0) {
putchar_unlocked('0' + (rev % 10));
rev /= 10;
}
while (0 != count) {
--count;
putchar_unlocked('0');
}
}
Когда число содержит слишком много цифр, его будет слишком сложно прочитать. Чтобы напечатать число в тысячах с чувствительным к локали форматом для 128-битных целых чисел, мы должны разделить 128-битное целое число на более мелкие части с количеством цифр, кратным 3. 128-битное целое число имеет максимум 39 цифр. Мы можем разделить его на 3 части с максимум 15 или 18 цифрами для каждой части. Таким образом, мы можем напечатать 128-битные целые числа в следующем формате:
1,234,567,890,000,000,123,456 (in en_US locale)
1.234.567.890.000.000.123.456 (in de_DE locale)
1 234 567 890 000 000 123 456 (in en_CA locale)
Для переносимости следующий код просто использует sprintf C, потому что локаль C++ не работает в gcc stdlib с платформой Windows.
#if (LONG_INT_BITS==32)
#define long_int_suffix "ll" // Windows long is 32 bit
#else
#define long_int_suffix "l" // Linux and the rest uses 64 bit
#endif
// assuming buffer is large enough for a 128 integer
// when grouping is true, numbers will be grouped in thousands by a separator
// of the current locale (by calling setlocale(LC_NUMERIC, locale_code);
size_t print_uint128(char* buffer, __uint128_t val, bool grouping)
{
// separate 128-bit integer into three parts: (high2, high1, low), each part may have maximum 18 digits
const uint64_t divisor = 1000000000000000000ULL;
__uint128_t high = val / divisor;
uint64_t high2 = high / divisor;
uint64_t high1 = high % divisor;
uint64_t low = val % divisor;
size_t length_printed = 0;
size_t section_length;
if (high2 > 0)
{
// print the first, the most significant part
length_printed = sprintf(buffer, grouping ? "%'" long_int_suffix "u" : "%" long_int_suffix "u", high2);
buffer += length_printed ;
}
if (high2 > 0 || high1 > 0)
{
// the second part, even when zero, needs to be printed if the first part has been printed
if (high2 > 0)
{
if (grouping)
{
// if grouping is required, add divisor in order to get proper thousand seperator inserted
// in leading zeros
high1 += divisor;
section_length = sprintf(buffer, "%'" long_int_suffix "u", high1);
}
else
{
section_length = sprintf(buffer, "%012" long_int_suffix "u", high1);
}
}
else
{
section_length = sprintf(buffer, grouping ? "%'" long_int_suffix "u" : "%" long_int_suffix "u", high1);
}
if (grouping && high2 > 0)
{
// move all digits in this part backward by one to remove the extra leading '1' printed due to the
// addition of the value divisor
memcpy(buffer, buffer+1, section_length);
--section_length;
}
buffer += section_length;
length_printed += section_length;
}
if (high2 > 0 || high1 > 0)
{
if (grouping)
{
// add divisor in order to get proper thousand seperator inserted in leading zeros
low += divisor;
section_length = sprintf(buffer, "%'" long_int_suffix "u", low);
// remove the extra leading '1'
memcpy(buffer, buffer+1, section_length);
--section_length;
}
else
{
section_length = sprintf(buffer, "%012" long_int_suffix "u", low);
}
}
else
{
section_length = sprintf(buffer, grouping ? "%'" long_int_suffix "u" : "%" long_int_suffix "u", low);
}
return length_printed += section_length ;
}
size_t print_int128(char* buffer, __int128_t val, bool grouping, bool print_positive_sign)
{
__uint128_t uval;
size_t sign_byte =0;
if (val < 0)
{
uval = -val;
*buffer++ = '-';
sign_byte = 1;
}
else
{
uval = val;
if (print_positive_sign)
{
*buffer++ = '+';
sign_byte = 1;
}
}
return print_uint128(buffer, uval, grouping) + sign_byte;
}
Это для C++, но я оставлю это здесь, так как я не нашел версию C++ этого вопроса для 128-битных целых без знака.
Вот простой, читаемый способ конвертировать uint128 в строку base-10 (которую вы можете затем распечатать или сделать как хотите):
std::string toString(__uint128_t num) {
std::string str;
do {
int digit = num % 10;
str = std::to_string(digit) + str;
num = (num - digit) / 10;
} while (num != 0);
return str;
}
При необходимости мы можем сделать это в несколько раз быстрее, получая цифры большими кусками вместо одного за раз. Но это требует, чтобы мы проверяли каждый блок на наличие лидирующих нулей, которые были потеряны, и добавляли их обратно:
std::string toString(__uint128_t num) {
auto tenPow19 = 10000000000000000000;
std::string str;
do {
uint64_t digits = num % tenPow19;
auto digitsStr = std::to_string(digits);
auto leading0s = (digits != num) ? std::string(19 - digitsStr.length(), '0') : "";
str = leading0s + digitsStr + str;
num = (num - digits) / tenPow19;
} while (num != 0);
return str;
}
Вы можете переопределить операторы cin и cout для работы с __int128_t. Вы должны преобразовывать только __int128_t в строки и строки cin/cout
typedef __int128_t lint;
istream& operator >> (istream &in, lint &x) {
string s;
in >> s;
for (lint i = s.size() - 1, p = 1; i >= 0; i--, p *= 10) x += p * (s[i] - '0');
return in;
}
ostream& operator << (ostream &out, lint x) {
string s;
while (x > 0) {
s.push_back(x % 10 + '0');
x /= 10;
}
reverse(s.begin(), s.end());
out << s;
return out;
}
Очень похоже на № 3
unsigned __int128 g = ...........;
printf ("g = 0x%lx%lx\r\n", (uint64_t) (g >> 64), (uint64_t) g);