mruby-sprintf: support dots expression of negative integer format

For specifiers assume unsigned integers, negative numbers show dots (..)
to indicate virtual infinite 1s at the MSB side of 2's compliment.
This commit is contained in:
Yukihiro "Matz" Matsumoto
2024-07-09 10:59:26 +09:00
parent 80fec16cb6
commit a77bc25a5f
2 changed files with 49 additions and 0 deletions
+38
View File
@@ -1626,3 +1626,41 @@ mrb_bint_hash(mrb_state *mrb, mrb_value x)
hash = mrb_byte_hash_step((uint8_t*)&b->mp.sn, sizeof(b->mp.sn), hash);
return mrb_int_value(mrb, hash);
}
mrb_value
mrb_bint_2comp(mrb_state *mrb, mrb_value x, mrb_int base)
{
struct RBigint *b = RBIGINT(x);
mrb_int i;
for (i=b->mp.sz-1; i>=0; i--) {
if (b->mp.p[i] > 0) break;
}
#ifdef __GNUC__
mrb_int dbits = __builtin_ctz(base);
#else
mrb_int dbits;
switch (base) {
case 16: dbits = 4; break;
case 8: dbits = 3; break;
case 2: dbits = 1; break;
}
#endif
mrb_int topbit = DIG_SIZE * (i+1) - lzb(b->mp.p[i]);
mrb_int nbits = (topbit / dbits + 1) * dbits;
mpz_t one;
mpz_t up;
struct RBigint *b2 = bint_new(mrb);
/* rounding up to nearest power of 2 */
mpz_init_set_int(mrb, &one, 1);
mpz_init(mrb, &up);
mpz_mul_2exp(mrb, &up, &one, nbits);
mpz_clear(mrb, &one);
mpz_add(mrb, &b2->mp, &up, &b->mp);
mpz_clear(mrb, &up);
return mrb_obj_value(b2);
}
+11
View File
@@ -17,6 +17,8 @@
#define BITSPERDIG MRB_INT_BIT
#define EXTENDSIGN(n, l) (((~0U << (n)) >> (((n)*(l)) % BITSPERDIG)) & ~(~0U << (n)))
mrb_value mrb_bint_2comp(mrb_state *mrb, mrb_value x, mrb_int base);
static char*
remove_sign_bits(char *str, int base)
{
@@ -594,6 +596,13 @@ retry:
#ifdef MRB_USE_BIGINT
case MRB_TT_BIGINT:
{
mrb_int n = (mrb_bint_cmp(mrb, val, mrb_fixnum_value(0)));
mrb_bool need_dots = ((flags & FPLUS) == 0) && (base == 16 || base == 8 || base == 2) && n < 0;
if (need_dots) {
val = mrb_bint_2comp(mrb, val, base);
dots = 1;
v = -1;
}
mrb_value str = mrb_bint_to_s(mrb, val, base);
s = RSTRING_PTR(str);
len = RSTRING_LEN(str);
@@ -644,7 +653,9 @@ retry:
len = (int)size;
}
#ifdef MRB_USE_BIGINT
str_skip:
#endif
switch (base) {
case 16:
fc = 'f'; break;