diff --git a/ChangeLog b/ChangeLog index 6bbd5f781..7216717b3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2006-04-22 Yoshinori K. Okuji + + * kern/misc.c (grub_lltoa): Rewritten the decimal conversion part, + as it was simply too buggy. + 2006-04-21 Yoshinori K. Okuji * kern/misc.c (grub_lltoa): New function. diff --git a/kern/misc.c b/kern/misc.c index fa1760815..fc61343c8 100644 --- a/kern/misc.c +++ b/kern/misc.c @@ -526,22 +526,33 @@ grub_lltoa (char *str, int c, unsigned long long n) /* BASE == 10 */ do { - unsigned high, low; - unsigned high_mod, low_mod; - unsigned d; - - high = (unsigned) (n >> 32); - low = (unsigned) (n & 0xffffffff); - high_mod = high % 10; - low_mod = low % 10; - /* 6 = (1 << 32) % 10 */ - d = (high_mod * 6 + low_mod) % 10; - *p++ = d + '0'; - - /* (HIGH_MOD << 31) / 5 = (HIGH_MOD << 32) / 10 */ - n = (((unsigned long long) (high / 10) << 32) - + ((high_mod << 31) / 5) - + low_mod / 10); + /* This algorithm is typically implemented by hardware. The idea + is to get the highest bit in N, 64 times, by keeping + upper(N * 2^i) = upper((Q * 10 + M) * 2^i), where upper + represents the high 64 bits in 128-bits space. */ + unsigned bits = sizeof (unsigned long long) * 8; + unsigned long long q = 0; + unsigned m = 0; + + while (bits--) + { + m <<= 1; + + if ((long long ) n < 0) + m |= 1; + + q <<= 1; + n <<= 1; + + if (m >= 10) + { + q |= 1; + m -= 10; + } + } + + *p++ = m + '0'; + n = q; } while (n);