Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #73281 > unrolled thread
| Started by | pozz <pozzugno@gmail.com> |
|---|---|
| First post | 2015-10-19 14:28 +0200 |
| Last post | 2015-10-20 10:09 -0700 |
| Articles | 20 on this page of 37 — 11 participants |
Back to article view | Back to comp.lang.c
How to encode/decode string of decimal digits pozz <pozzugno@gmail.com> - 2015-10-19 14:28 +0200
Re: How to encode/decode string of decimal digits Bartc <bc@freeuk.com> - 2015-10-19 14:07 +0100
Re: How to encode/decode string of decimal digits pozz <pozzugno@gmail.com> - 2015-10-19 15:58 +0200
Re: How to encode/decode string of decimal digits Bartc <bc@freeuk.com> - 2015-10-19 15:22 +0100
Re: How to encode/decode string of decimal digits Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-10-19 15:58 +0100
Re: How to encode/decode string of decimal digits Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-10-19 22:28 +0100
Re: How to encode/decode string of decimal digits Eric Sosman <esosman@comcast-dot-net.invalid> - 2015-10-19 13:30 -0400
Re: How to encode/decode string of decimal digits Robert Wessel <robertwessel2@yahoo.com> - 2015-10-19 16:18 -0500
Re: How to encode/decode string of decimal digits Eric Sosman <esosman@comcast-dot-net.invalid> - 2015-10-19 21:38 -0400
Re: How to encode/decode string of decimal digits Jorgen Grahn <grahn+nntp@snipabacken.se> - 2015-10-20 08:00 +0000
Re: How to encode/decode string of decimal digits Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-10-19 15:50 +0100
Re: How to encode/decode string of decimal digits Keith Thompson <kst-u@mib.org> - 2015-10-19 09:01 -0700
Re: How to encode/decode string of decimal digits Tim Rentsch <txr@alumni.caltech.edu> - 2015-10-19 12:11 -0700
Re: How to encode/decode string of decimal digits pozz <pozzugno@gmail.com> - 2015-10-20 13:51 +0200
Re: How to encode/decode string of decimal digits Bartc <bc@freeuk.com> - 2015-10-20 13:38 +0100
Re: How to encode/decode string of decimal digits glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-10-20 16:05 +0000
Re: How to encode/decode string of decimal digits pozz <pozzugno@gmail.com> - 2015-10-21 09:29 +0200
Re: How to encode/decode string of decimal digits Bartc <bc@freeuk.com> - 2015-10-21 11:27 +0100
Re: How to encode/decode string of decimal digits pozz <pozzugno@gmail.com> - 2015-10-21 17:55 +0200
Re: How to encode/decode string of decimal digits Eric Sosman <esosman@comcast-dot-net.invalid> - 2015-10-21 08:03 -0400
Re: How to encode/decode string of decimal digits pozz <pozzugno@gmail.com> - 2015-10-21 17:53 +0200
Re: How to encode/decode string of decimal digits supercat@casperkitty.com - 2015-10-21 09:37 -0700
Re: How to encode/decode string of decimal digits David Brown <david.brown@hesbynett.no> - 2015-10-22 11:47 +0200
Re: How to encode/decode string of decimal digits pozz <pozzugno@gmail.com> - 2015-10-22 13:44 +0200
Re: How to encode/decode string of decimal digits Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-10-22 13:45 +0100
Re: How to encode/decode string of decimal digits Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-10-22 15:51 +0100
Re: How to encode/decode string of decimal digits supercat@casperkitty.com - 2015-10-22 09:31 -0700
Re: How to encode/decode string of decimal digits Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-10-22 20:48 +0100
Re: How to encode/decode string of decimal digits pozz <pozzugno@gmail.com> - 2015-10-23 10:14 +0200
Re: How to encode/decode string of decimal digits Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-10-23 11:14 +0100
Re: How to encode/decode string of decimal digits Bartc <bc@freeuk.com> - 2015-10-23 11:47 +0100
Re: How to encode/decode string of decimal digits Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-10-23 13:22 +0100
Re: How to encode/decode string of decimal digits David Brown <david.brown@hesbynett.no> - 2015-10-22 15:07 +0200
Re: How to encode/decode string of decimal digits glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-10-22 22:05 +0000
Re: How to encode/decode string of decimal digits pozz <pozzugno@gmail.com> - 2015-10-23 10:15 +0200
Re: How to encode/decode string of decimal digits glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-10-19 22:02 +0000
Re: How to encode/decode string of decimal digits supercat@casperkitty.com - 2015-10-20 10:09 -0700
Page 1 of 2 [1] 2 Next page →
| From | pozz <pozzugno@gmail.com> |
|---|---|
| Date | 2015-10-19 14:28 +0200 |
| Subject | How to encode/decode string of decimal digits |
| Message-ID | <n02ni7$k3q$1@dont-email.me> |
I have to manage string of decimal digits 0-9. They aren't integer
numbers, because the string "01234" is completely different than the
number 1234.
I have a very limited embedded plaftorm, so I'm searching for a simple
compression/decompression method of a single string.
I thought to use a uint64_t to store a 16-digits string. One byte (the
LSB) of uint64_t is used to save the number of leading zeros.
uint64_t
compress(const char *s)
{
uint64_t ret;
unsigned char nz;
ret = strtoull(s, NULL, 10);
for (nz = 0; *s++ == '0'; nz++)
;
ret = (ret << 8) + nz;
return ret;
}
void
decompress(char *dst, uint64_t n)
{
unsigned char nz;
nz = n & 0xFF;
n >>= 8;
memset(dst, '0', nz);
if (n > 0) {
sprintf(&dst[nz], "%lld", n);
} else {
dst[nz] = '\0';
}
}
In this way I can save one 16-digits string in 8 bytes.
Do you think of a better method?
[toc] | [next] | [standalone]
| From | Bartc <bc@freeuk.com> |
|---|---|
| Date | 2015-10-19 14:07 +0100 |
| Message-ID | <n02pr6$sqa$1@dont-email.me> |
| In reply to | #73281 |
On 19/10/2015 13:28, pozz wrote:
> I have to manage string of decimal digits 0-9. They aren't integer
> numbers, because the string "01234" is completely different than the
> number 1234.
>
> I have a very limited embedded plaftorm, so I'm searching for a simple
> compression/decompression method of a single string.
>
> I thought to use a uint64_t to store a 16-digits string. One byte (the
> LSB) of uint64_t is used to save the number of leading zeros.
>
> uint64_t
> compress(const char *s)
> {
> uint64_t ret;
> unsigned char nz;
>
> ret = strtoull(s, NULL, 10);
> for (nz = 0; *s++ == '0'; nz++)
> ;
> ret = (ret << 8) + nz;
> return ret;
> }
>
> void
> decompress(char *dst, uint64_t n)
> {
> unsigned char nz;
>
> nz = n & 0xFF;
> n >>= 8;
> memset(dst, '0', nz);
> if (n > 0) {
> sprintf(&dst[nz], "%lld", n);
> } else {
> dst[nz] = '\0';
> }
> }
>
> In this way I can save one 16-digits string in 8 bytes.
>
> Do you think of a better method?
How many leading zeros are there likely to be? And how important that
they be recovered?
Also, is performance important? Because above you are converting strings
into binary, and converting from binary into strings, all of which can
require quite a bit of arithmetic to do.
Assuming you can have up to 255 leading zeros, and up to 16 significant
digits, your basic data is 17 bytes (16 chars + 1 byte count, no
terminator). Your method above reduces that to 8 bytes. Not a bad
reduction, but a lot of work, and we don't know whether space is more
important or speed.
You can reduce that to 9 bytes by using a 4-bit representation for each
digit (BCD, 2 digits per byte), an easier conversion. Or 8 bytes if your
forget the leading zero count.
There are far more elaborate methods to reduce below 8 bytes per string,
but they can depend on how random the strings are.
--
Bartc
[toc] | [prev] | [next] | [standalone]
| From | pozz <pozzugno@gmail.com> |
|---|---|
| Date | 2015-10-19 15:58 +0200 |
| Message-ID | <n02sqq$8sg$1@dont-email.me> |
| In reply to | #73288 |
Il 19/10/2015 15:07, Bartc ha scritto:
> On 19/10/2015 13:28, pozz wrote:
>> I have to manage string of decimal digits 0-9. They aren't integer
>> numbers, because the string "01234" is completely different than the
>> number 1234.
>>
>> I have a very limited embedded plaftorm, so I'm searching for a simple
>> compression/decompression method of a single string.
>>
>> I thought to use a uint64_t to store a 16-digits string. One byte (the
>> LSB) of uint64_t is used to save the number of leading zeros.
>>
>> uint64_t
>> compress(const char *s)
>> {
>> uint64_t ret;
>> unsigned char nz;
>>
>> ret = strtoull(s, NULL, 10);
>> for (nz = 0; *s++ == '0'; nz++)
>> ;
>> ret = (ret << 8) + nz;
>> return ret;
>> }
>>
>> void
>> decompress(char *dst, uint64_t n)
>> {
>> unsigned char nz;
>>
>> nz = n & 0xFF;
>> n >>= 8;
>> memset(dst, '0', nz);
>> if (n > 0) {
>> sprintf(&dst[nz], "%lld", n);
>> } else {
>> dst[nz] = '\0';
>> }
>> }
>>
>> In this way I can save one 16-digits string in 8 bytes.
>>
>> Do you think of a better method?
>
> How many leading zeros are there likely to be?
16 digits, including leading zeros. So there could be 16 leading zero at
maximum (the exceptional case of the string "0000000000000000").
I don't know what could be the statistics of those strings, so I have to
suppose all the strings have the same probability.
> And how important that they be recovered?
It's a must.
> Also, is performance important? Because above you are converting strings
> into binary, and converting from binary into strings, all of which can
> require quite a bit of arithmetic to do.
This is correct. I'm using a Cortex-M0 MCU.
> Assuming you can have up to 255 leading zeros, and up to 16 significant
> digits, your basic data is 17 bytes (16 chars + 1 byte count, no
> terminator). Your method above reduces that to 8 bytes. Not a bad
> reduction, but a lot of work, and we don't know whether space is more
> important or speed.
>
> You can reduce that to 9 bytes by using a 4-bit representation for each
> digit (BCD, 2 digits per byte), an easier conversion. Or 8 bytes if your
> forget the leading zero count.
Yes, BCD is another possibility. I have to make some measure.
> There are far more elaborate methods to reduce below 8 bytes per string,
> but they can depend on how random the strings are.
[toc] | [prev] | [next] | [standalone]
| From | Bartc <bc@freeuk.com> |
|---|---|
| Date | 2015-10-19 15:22 +0100 |
| Message-ID | <n02u73$erm$1@dont-email.me> |
| In reply to | #73292 |
On 19/10/2015 14:58, pozz wrote: > Il 19/10/2015 15:07, Bartc ha scritto: >> On 19/10/2015 13:28, pozz wrote: >>> I thought to use a uint64_t to store a 16-digits string. One byte (the >>> LSB) of uint64_t is used to save the number of leading zeros. >> How many leading zeros are there likely to be? > > 16 digits, including leading zeros. So there could be 16 leading zero at > maximum (the exceptional case of the string "0000000000000000"). > I don't know what could be the statistics of those strings, so I have to > suppose all the strings have the same probability. > > > And how important that they be recovered? > > It's a must. > Yes, BCD is another possibility. I have to make some measure. So, leading zeros are important (and that "00123" is different from "0123"), and there can be up to 16 characters. That means that without reduction, each value can take up to 16 bytes. (Possibly with a 17th byte to terminate, but that is easy to avoid.) Then BCD is the obvious easy choice as any string will fit into an 8-byte integer (and Eric said you can use a non-digit code to represent a space, or perhaps as a terminator). Would that be sufficient? If the strings vary in length, and they are mostly short, then you might be better off keeping them as strings. Another, more fiddly way is to have variable-length BCD, which will average half the length of the strings. Also, how are these different values stored; in an array? A linked list? How are they searched? There might be overheads we don't know about. Would it be possible to take advantage of duplicate strings? Will leading zeros figure a lot? (In telephone numbers, there might be only 0, 1 or 2.) If more than that, you can use your idea of storing a count for those, but that works best with a variable-length method. -- Bartc
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2015-10-19 15:58 +0100 |
| Message-ID | <878u6yzz01.fsf@bsb.me.uk> |
| In reply to | #73292 |
pozz <pozzugno@gmail.com> writes: <snip> > Yes, BCD is another possibility. I have to make some measure. Another BCD option is to encode every 3 decimal digits in 10 bits. You will be able to squeeze 18 digits in this way if you make special provision for the case of 16, 17 and 18 leading zeros. <snip> -- Ben.
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2015-10-19 22:28 +0100 |
| Message-ID | <87wpuiy2e6.fsf@bsb.me.uk> |
| In reply to | #73298 |
Ben Bacarisse <ben.usenet@bsb.me.uk> writes: > pozz <pozzugno@gmail.com> writes: > <snip> >> Yes, BCD is another possibility. I have to make some measure. > > Another BCD option is to encode every 3 decimal digits in 10 bits. You > will be able to squeeze 18 digits in this way if you make special > provision for the case of 16, 17 and 18 leading zeros. ... and another thought combining two other ideas: you get 18 digits quite simply if you encode 9 base 121 digits in 7 bits each. That is every 7 bit represents two base 11 digits. (Base 11 allows for '0' to map to 1, '1' to 2 and so on.) -- Ben.
[toc] | [prev] | [next] | [standalone]
| From | Eric Sosman <esosman@comcast-dot-net.invalid> |
|---|---|
| Date | 2015-10-19 13:30 -0400 |
| Message-ID | <n02r4q$27q$1@dont-email.me> |
| In reply to | #73281 |
On 10/19/2015 8:28 AM, pozz wrote:
> I have to manage string of decimal digits 0-9. They aren't integer
> numbers, because the string "01234" is completely different than the
> number 1234.
>
> I have a very limited embedded plaftorm, so I'm searching for a simple
> compression/decompression method of a single string.
>
> I thought to use a uint64_t to store a 16-digits string. One byte (the
> LSB) of uint64_t is used to save the number of leading zeros.
>
> uint64_t
> compress(const char *s)
> {
> uint64_t ret;
> unsigned char nz;
>
> ret = strtoull(s, NULL, 10);
Aside: No error check? I hope you *know* the input is valid...
> for (nz = 0; *s++ == '0'; nz++)
> ;
> ret = (ret << 8) + nz;
> return ret;
> }
>
> void
> decompress(char *dst, uint64_t n)
> {
> unsigned char nz;
>
> nz = n & 0xFF;
> n >>= 8;
> memset(dst, '0', nz);
> if (n > 0) {
> sprintf(&dst[nz], "%lld", n);
> } else {
> dst[nz] = '\0';
> }
> }
>
> In this way I can save one 16-digits string in 8 bytes.
>
> Do you think of a better method?
Much depends on your definition of "better." Are you looking for
space efficiency (maximum compression), or speed, or simplicity, or ...?
If it's simplicity you want, and you're sure you won't need more
than 16 decimal digits, I'd suggest encoding each digit as a four-bit
value and just packing them together. The decimal digits use only ten
of the sixteen possible four-bit values, so one of the unused nybbles
could serve as a "no digit" or "blank" indicator.
For tighter compression, you might try padding the string with X's
(before or after the digits; your choice) and converting the whole
thing as a base-eleven value where digits 0-9 stand for themselves and
the X stands for ten. This would encode strings of up to 18 digits
in a 64-bit quantity, without much complexity.
Tighter compression schemes certainly exist -- but before we start
exploring them, tell us more about what "better" means to you.
--
esosman@comcast-dot-net.invalid
"Don't be afraid of work. Make work afraid of you." -- TLM
[toc] | [prev] | [next] | [standalone]
| From | Robert Wessel <robertwessel2@yahoo.com> |
|---|---|
| Date | 2015-10-19 16:18 -0500 |
| Message-ID | <mhma2bpi45ufs2epmt6jkg3br5p83b5l58@4ax.com> |
| In reply to | #73291 |
On Mon, 19 Oct 2015 13:30:00 -0400, Eric Sosman
<esosman@comcast-dot-net.invalid> wrote:
>On 10/19/2015 8:28 AM, pozz wrote:
>> I have to manage string of decimal digits 0-9. They aren't integer
>> numbers, because the string "01234" is completely different than the
>> number 1234.
>>
>> I have a very limited embedded plaftorm, so I'm searching for a simple
>> compression/decompression method of a single string.
>>
>> I thought to use a uint64_t to store a 16-digits string. One byte (the
>> LSB) of uint64_t is used to save the number of leading zeros.
>>
>> uint64_t
>> compress(const char *s)
>> {
>> uint64_t ret;
>> unsigned char nz;
>>
>> ret = strtoull(s, NULL, 10);
>
> Aside: No error check? I hope you *know* the input is valid...
>
>> for (nz = 0; *s++ == '0'; nz++)
>> ;
>> ret = (ret << 8) + nz;
>> return ret;
>> }
>>
>> void
>> decompress(char *dst, uint64_t n)
>> {
>> unsigned char nz;
>>
>> nz = n & 0xFF;
>> n >>= 8;
>> memset(dst, '0', nz);
>> if (n > 0) {
>> sprintf(&dst[nz], "%lld", n);
>> } else {
>> dst[nz] = '\0';
>> }
>> }
>>
>> In this way I can save one 16-digits string in 8 bytes.
>>
>> Do you think of a better method?
>
> Much depends on your definition of "better." Are you looking for
>space efficiency (maximum compression), or speed, or simplicity, or ...?
>
> If it's simplicity you want, and you're sure you won't need more
>than 16 decimal digits, I'd suggest encoding each digit as a four-bit
>value and just packing them together. The decimal digits use only ten
>of the sixteen possible four-bit values, so one of the unused nybbles
>could serve as a "no digit" or "blank" indicator.
Perhaps a variation on this. Use one of the unused nibbles as a
string terminator. So encode "1234" and "012345" as 0x1234FFFF and
0x01234FFF (32 bit encoded values for illustration). If it's more
convenient, put the terminators on the other side (0xffff1234 and
0xfff012345).
Using the latter encoding, something like:
uint64_t compress(const char *s)
{
uint64_t ret = 0xffffffffffffffff;
while (*s)
{
ret << 4;
ret += (*s) - '0';
s++;
}
return ret;
}
void decompress(char *dst, uint64_t n)
{
int i;
unsigned t;
for (i=0; i<16; i++)
{
t = (n & 0xf000000000000000) >> 60;
if (t == 0xf)
break;
*dst = t + '0';
dst++;
n << 4;
}
*dst = 0;
}
[toc] | [prev] | [next] | [standalone]
| From | Eric Sosman <esosman@comcast-dot-net.invalid> |
|---|---|
| Date | 2015-10-19 21:38 -0400 |
| Message-ID | <n03nod$rnu$1@dont-email.me> |
| In reply to | #73341 |
On 10/19/2015 5:18 PM, Robert Wessel wrote:
> On Mon, 19 Oct 2015 13:30:00 -0400, Eric Sosman
> <esosman@comcast-dot-net.invalid> wrote:
>
>> On 10/19/2015 8:28 AM, pozz wrote:
>>> I have to manage string of decimal digits 0-9. They aren't integer
>>> numbers, because the string "01234" is completely different than the
>>> number 1234.
>>>
>>> I have a very limited embedded plaftorm, so I'm searching for a simple
>>> compression/decompression method of a single string.
>>>
>>> I thought to use a uint64_t to store a 16-digits string. One byte (the
>>> LSB) of uint64_t is used to save the number of leading zeros.
>>>
>>> uint64_t
>>> compress(const char *s)
>>> {
>>> uint64_t ret;
>>> unsigned char nz;
>>>
>>> ret = strtoull(s, NULL, 10);
>>
>> Aside: No error check? I hope you *know* the input is valid...
>>
>>> for (nz = 0; *s++ == '0'; nz++)
>>> ;
>>> ret = (ret << 8) + nz;
>>> return ret;
>>> }
>>>
>>> void
>>> decompress(char *dst, uint64_t n)
>>> {
>>> unsigned char nz;
>>>
>>> nz = n & 0xFF;
>>> n >>= 8;
>>> memset(dst, '0', nz);
>>> if (n > 0) {
>>> sprintf(&dst[nz], "%lld", n);
>>> } else {
>>> dst[nz] = '\0';
>>> }
>>> }
>>>
>>> In this way I can save one 16-digits string in 8 bytes.
>>>
>>> Do you think of a better method?
>>
>> Much depends on your definition of "better." Are you looking for
>> space efficiency (maximum compression), or speed, or simplicity, or ...?
>>
>> If it's simplicity you want, and you're sure you won't need more
>> than 16 decimal digits, I'd suggest encoding each digit as a four-bit
>> value and just packing them together. The decimal digits use only ten
>> of the sixteen possible four-bit values, so one of the unused nybbles
>> could serve as a "no digit" or "blank" indicator.
>
> Perhaps a variation on this. Use one of the unused nibbles as a
> string terminator. So encode "1234" and "012345" as 0x1234FFFF and
> 0x01234FFF (32 bit encoded values for illustration). If it's more
> convenient, put the terminators on the other side (0xffff1234 and
> 0xfff012345).
That's how I expected the "no digit" to be used: As a way to
distinguish between a leading zero and a nothing.
It's not the most space-efficient encoding possible, since it's
able to encode configurations with embedded nothings: If the input
is a digit string, all the nothings appear at the ends. (Deciding
how the nothings are divided between left and right is left as a
particularly vacuous exercise for the reader. :)
The thread has already produced at least four simple ways to
encode 1-18 decimal digits in a uint64_t, so I doubt it's worth
searching for super-dense encodings until and unless the O.P. says
he really needs them. (It's a sad fact that denser encodings usually
require bigger and slower encoders and decoders. The O.P. indicates
that he's writing for a machine of limited capability, so the really
fancy approaches may not be available to him.)
--
esosman@comcast-dot-net.invalid
"Don't be afraid of work. Make work afraid of you." -- TLM
[toc] | [prev] | [next] | [standalone]
| From | Jorgen Grahn <grahn+nntp@snipabacken.se> |
|---|---|
| Date | 2015-10-20 08:00 +0000 |
| Message-ID | <slrnn2bt4l.5q5.grahn+nntp@frailea.sa.invalid> |
| In reply to | #73291 |
On Mon, 2015-10-19, Eric Sosman wrote:
> On 10/19/2015 8:28 AM, pozz wrote:
>> I have to manage string of decimal digits 0-9. They aren't integer
>> numbers, because the string "01234" is completely different than the
>> number 1234.
>>
>> I have a very limited embedded plaftorm, so I'm searching for a simple
>> compression/decompression method of a single string.
...
>> Do you think of a better method?
>
> Much depends on your definition of "better." Are you looking for
> space efficiency (maximum compression), or speed, or simplicity, or ...?
>
> If it's simplicity you want, and you're sure you won't need more
> than 16 decimal digits, I'd suggest encoding each digit as a four-bit
> value and just packing them together. The decimal digits use only ten
> of the sixteen possible four-bit values, so one of the unused nybbles
> could serve as a "no digit" or "blank" indicator.
A thought related to that: it's an interesting little problem, but all
we really know about the conditions around it is (unless I missed
something) the phrase "a very limited embedded plaftorm" above. That's
very subjective ...
Is the OP sure that this isn't enough?
struct my_number {
char val[16+1]; // unfortunate that it wasn't 15+1
};
Not that the BCD solution above is a lot more complicated; both end up
with a struct which is convenient to handle, nothing variable-length
and nothing that needs malloc(). Both can have invalid states which
you need to prevent (non-digits mixed with the digits). But the
latter is compatible with C strings so you have access to printf(),
strlen() and so on.
> Tighter compression schemes certainly exist -- but before we start
> exploring them, tell us more about what "better" means to you.
>
/Jorgen
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2015-10-19 15:50 +0100 |
| Message-ID | <87eggqzzcz.fsf@bsb.me.uk> |
| In reply to | #73281 |
pozz <pozzugno@gmail.com> writes: > I have to manage string of decimal digits 0-9. They aren't integer > numbers, because the string "01234" is completely different than the > number 1234. > > I have a very limited embedded plaftorm, so I'm searching for a simple > compression/decompression method of a single string. > > I thought to use a uint64_t to store a 16-digits string. One byte (the > LSB) of uint64_t is used to save the number of leading zeros. Just one remark about that... You don't need a whole byte for the count and if you use a variable length, I think you can store any 18 decimal digit number. The key observation being the large numbers can't have many leading zeros. <snip> -- Ben.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-10-19 09:01 -0700 |
| Message-ID | <lnh9lmzw2p.fsf@kst-u.example.com> |
| In reply to | #73281 |
pozz <pozzugno@gmail.com> writes:
> I have to manage string of decimal digits 0-9. They aren't integer
> numbers, because the string "01234" is completely different than the
> number 1234.
Are the strings "01234" and "1234" distinct? What is the range of
permitted lengths of these strings?
[...]
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2015-10-19 12:11 -0700 |
| Message-ID | <kfnvba2fzb6.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #73281 |
pozz <pozzugno@gmail.com> writes:
> I have to manage string of decimal digits 0-9. They aren't integer
> numbers, because the string "01234" is completely different than the
> number 1234.
>
> I have a very limited embedded plaftorm, so I'm searching for a simple
> compression/decompression method of a single string.
>
> I thought to use a uint64_t to store a 16-digits string. One byte (the
> LSB) of uint64_t is used to save the number of leading zeros.
>
> uint64_t
> compress(const char *s)
> {
> uint64_t ret;
> unsigned char nz;
>
> ret = strtoull(s, NULL, 10);
> for (nz = 0; *s++ == '0'; nz++)
> ;
> ret = (ret << 8) + nz;
> return ret;
> }
>
> void
> decompress(char *dst, uint64_t n)
> {
> unsigned char nz;
>
> nz = n & 0xFF;
> n >>= 8;
> memset(dst, '0', nz);
> if (n > 0) {
> sprintf(&dst[nz], "%lld", n);
> } else {
> dst[nz] = '\0';
> }
> }
>
> In this way I can save one 16-digits string in 8 bytes.
>
> Do you think of a better method?
Similar idea to above, just slightly simpler implementation:
unsigned long long
compress( const char *s ){
return strtoull( s, 0, 10 ) << 5 | strlen( s );
}
void
decompress( char *s, unsigned long long n ){
sprintf( s, "%.*llu", (int){ n & 31 }, n >> 5 );
}
I didn't do extensive testing but based on the little I
did I think this works for decimal strings from 0 up to 17
digits in length, including leading zeroes.
[toc] | [prev] | [next] | [standalone]
| From | pozz <pozzugno@gmail.com> |
|---|---|
| Date | 2015-10-20 13:51 +0200 |
| Message-ID | <n059nk$f4g$1@dont-email.me> |
| In reply to | #73327 |
I answer here for everyone. Thank you very much for your suggestions.
Il 19/10/2015 21:11, Tim Rentsch ha scritto:
> [...]
> Similar idea to above, just slightly simpler implementation:
>
> unsigned long long
> compress( const char *s ){
> return strtoull( s, 0, 10 ) << 5 | strlen( s );
> }
>
> void
> decompress( char *s, unsigned long long n ){
> sprintf( s, "%.*llu", (int){ n & 31 }, n >> 5 );
> }
>
> I didn't do extensive testing but based on the little I
> did I think this works for decimal strings from 0 up to 17
> digits in length, including leading zeroes.
I think this is the best method. I have to make some measurements, but
it is sufficiently compact and simple to write and understand.
The strings will be stored in a linear array and they can be added *and*
removed. So I have to understand if a position is empty.
With this method is sufficient to compare against zero.
I have to search for a number in the list, and I'm going to process each
number from the beginning to the end. The comparing between two strings
will be a simple compare between two uint64_t numbers.
I know uint64_t is managed by "software" functions, but I think they are
highly optimized from the compiler/library supplier for my architecture.
Cortex-M0 is a 32-bit MCU, so I think uint64_t numbers would be managed
without complex operations.
I think BCD representation is more complex without compressing much more.
[toc] | [prev] | [next] | [standalone]
| From | Bartc <bc@freeuk.com> |
|---|---|
| Date | 2015-10-20 13:38 +0100 |
| Message-ID | <n05ch6$q5i$1@dont-email.me> |
| In reply to | #73394 |
On 20/10/2015 12:51, pozz wrote:
> I answer here for everyone. Thank you very much for your suggestions.
>
> Il 19/10/2015 21:11, Tim Rentsch ha scritto:
> > [...]
>> Similar idea to above, just slightly simpler implementation:
>>
>> unsigned long long
>> compress( const char *s ){
>> return strtoull( s, 0, 10 ) << 5 | strlen( s );
>> }
>>
>> void
>> decompress( char *s, unsigned long long n ){
>> sprintf( s, "%.*llu", (int){ n & 31 }, n >> 5 );
>> }
>>
>> I didn't do extensive testing but based on the little I
>> did I think this works for decimal strings from 0 up to 17
>> digits in length, including leading zeroes.
>
> I think this is the best method. I have to make some measurements, but
> it is sufficiently compact and simple to write and understand.
>
> The strings will be stored in a linear array and they can be added *and*
> removed. So I have to understand if a position is empty.
> With this method is sufficient to compare against zero.
>
> I have to search for a number in the list, and I'm going to process each
> number from the beginning to the end. The comparing between two strings
> will be a simple compare between two uint64_t numbers.
>
> I know uint64_t is managed by "software" functions, but I think they are
> highly optimized from the compiler/library supplier for my architecture.
> Cortex-M0 is a 32-bit MCU, so I think uint64_t numbers would be managed
> without complex operations.
>
> I think BCD representation is more complex without compressing much more.
BCD is simpler. But you don't see the complexity of string <-> binary
conversion because it's inside those library functions.
BCD 'compression' is a bit less than using binary, but in both cases you
will anyway be reducing 16 bytes of data to 8 bytes. A 16-char string
could also be directly stored, without too much difficult, as two 64-bit
values. (And most of the time you will probably only have to compare one.)
But presumably the overheads of decimal/binary conversion are not a problem.
--
Bartc
[toc] | [prev] | [next] | [standalone]
| From | glen herrmannsfeldt <gah@ugcs.caltech.edu> |
|---|---|
| Date | 2015-10-20 16:05 +0000 |
| Message-ID | <n05onn$ln4$1@speranza.aioe.org> |
| In reply to | #73397 |
Bartc <bc@freeuk.com> wrote:
> On 20/10/2015 12:51, pozz wrote:
>> I answer here for everyone. Thank you very much for your suggestions.
(snip)
>>> unsigned long long
>>> compress( const char *s ){
>>> return strtoull( s, 0, 10 ) << 5 | strlen( s );
>>> }
(snip)
>> I think this is the best method. I have to make some measurements, but
>> it is sufficiently compact and simple to write and understand.
If you already have strtoull() and sprintf(), this is pretty compact
to write. They do tend to be slow, as C library functions go.
>> The strings will be stored in a linear array and they can be added *and*
>> removed. So I have to understand if a position is empty.
>> With this method is sufficient to compare against zero.
>> I have to search for a number in the list, and I'm going to process each
>> number from the beginning to the end. The comparing between two strings
>> will be a simple compare between two uint64_t numbers.
If you never use them for their numerical value, the conversion
between decimal digits and the internal binary representation is
not needed, and slow.
You don't mention doing arithmetic on them, just considering them
as digit strings.
>> I know uint64_t is managed by "software" functions, but I think they are
>> highly optimized from the compiler/library supplier for my architecture.
>> Cortex-M0 is a 32-bit MCU, so I think uint64_t numbers would be managed
>> without complex operations.
>> I think BCD representation is more complex without compressing much more.
> BCD is simpler. But you don't see the complexity of string <-> binary
> conversion because it's inside those library functions.
It does depend on how often that conversion is done.
On less than 64 bit processors, uint64_t can be slow, but usually
add, subtract, and assignment are still fast enough. Multiply and
divide might be a lot slower.
> BCD 'compression' is a bit less than using binary, but in both cases you
> will anyway be reducing 16 bytes of data to 8 bytes. A 16-char string
> could also be directly stored, without too much difficult, as two 64-bit
> values. (And most of the time you will probably only have to compare one.)
If you never need the binary value, I suspect the bcd form is easier
to process and faster. If you rarely need the binary value, it might
still be faster.
> But presumably the overheads of decimal/binary conversion are
> not a problem.
-- glen
[toc] | [prev] | [next] | [standalone]
| From | pozz <pozzugno@gmail.com> |
|---|---|
| Date | 2015-10-21 09:29 +0200 |
| Message-ID | <n07ep5$oi1$1@dont-email.me> |
| In reply to | #73397 |
Il 20/10/2015 14:38, Bartc ha scritto:
> On 20/10/2015 12:51, pozz wrote:
>> I answer here for everyone. Thank you very much for your suggestions.
>>
>> Il 19/10/2015 21:11, Tim Rentsch ha scritto:
>> > [...]
>>> Similar idea to above, just slightly simpler implementation:
>>>
>>> unsigned long long
>>> compress( const char *s ){
>>> return strtoull( s, 0, 10 ) << 5 | strlen( s );
>>> }
>>>
>>> void
>>> decompress( char *s, unsigned long long n ){
>>> sprintf( s, "%.*llu", (int){ n & 31 }, n >> 5 );
>>> }
>>>
>>> I didn't do extensive testing but based on the little I
>>> did I think this works for decimal strings from 0 up to 17
>>> digits in length, including leading zeroes.
>>
>> I think this is the best method. I have to make some measurements, but
>> it is sufficiently compact and simple to write and understand.
>>
>> The strings will be stored in a linear array and they can be added *and*
>> removed. So I have to understand if a position is empty.
>> With this method is sufficient to compare against zero.
>>
>> I have to search for a number in the list, and I'm going to process each
>> number from the beginning to the end. The comparing between two strings
>> will be a simple compare between two uint64_t numbers.
>>
>> I know uint64_t is managed by "software" functions, but I think they are
>> highly optimized from the compiler/library supplier for my architecture.
>> Cortex-M0 is a 32-bit MCU, so I think uint64_t numbers would be managed
>> without complex operations.
>>
>> I think BCD representation is more complex without compressing much more.
>
> BCD is simpler. But you don't see the complexity of string <-> binary
> conversion because it's inside those library functions.
In my case, I will have an array of maximum 500-1000 "digit strings"
saved in Flash memory, in compressed form. The user sometime adds and
remove a single string, but this operation is rare, so this could be a
little slow, even some seconds.
The frequent operation will be searching a "digit strings" in the array,
so comparing the reference string against each string in the array.
If uint64_t integer is used as the encoding format of a "digit string",
the compare is very simple, it's just a comparison between two 64-bits
integers. On a 32-bits architecture, I think it will be a comparison
between two 32-bits registers. I think it's very simple.
If a variable length BCD string is used, I think the comparison process
is slower. I have to compare a byte against a byte, but I have to check
the end of the string.
Or I *need* to fill *all* the trailing nibbles with the terminator BCD
tag during encoding, for example 0xF. Only in this case, the comparison
is reduced to a memcmp() between two 8-bytes arrays.
"1234" -> 0x12, 0x34, 0xFF, 0xFF, ...
Another small benefit of uint64_t is this is able to encode a longer
string (maybe up to 17-18 digits).
Again a small benefit, the encode() and decode() functions are trivial
to read and write.
> BCD 'compression' is a bit less than using binary, but in both cases you
> will anyway be reducing 16 bytes of data to 8 bytes. A 16-char string
> could also be directly stored, without too much difficult, as two 64-bit
> values. (And most of the time you will probably only have to compare one.)
>
> But presumably the overheads of decimal/binary conversion are not a
> problem.
[toc] | [prev] | [next] | [standalone]
| From | Bartc <bc@freeuk.com> |
|---|---|
| Date | 2015-10-21 11:27 +0100 |
| Message-ID | <n07p65$rgh$1@dont-email.me> |
| In reply to | #73461 |
On 21/10/2015 08:29, pozz wrote: > Il 20/10/2015 14:38, Bartc ha scritto: >> BCD is simpler. But you don't see the complexity of string <-> binary >> conversion because it's inside those library functions. > > In my case, I will have an array of maximum 500-1000 "digit strings" > saved in Flash memory, in compressed form. The user sometime adds and > remove a single string, but this operation is rare, so this could be a > little slow, even some seconds. > The frequent operation will be searching a "digit strings" in the array, > so comparing the reference string against each string in the array. So the search method is more important. I don't know how often you need to search, or how slow the cortex-m0 is, but if you're using a linear search, and it's a bottleneck, you might want to look at that. (With the right search method, you can keep the data as strings, and it might still be faster than a linear search using uint64_t > > If uint64_t integer is used as the encoding format of a "digit string", > the compare is very simple, it's just a comparison between two 64-bits > integers. On a 32-bits architecture, I think it will be a comparison > between two 32-bits registers. I think it's very simple. > > If a variable length BCD string is used, I think the comparison process > is slower. I have to compare a byte against a byte, but I have to check > the end of the string. That depends. If you store the length with the BCD (as a leading byte is simplest), and do a one-time strlen() on the reference string, then you can quickly check the length first, using a single byte comparison. That can eliminate most full comparisons if the lengths are mostly variable. But not if they are 90% the same. But, anything involving variable length entries will be harder to index if random access is involved (eg. not using a linear search), and harder to replace one entry with another of a different length. > Or I *need* to fill *all* the trailing nibbles with the terminator BCD > tag during encoding, for example 0xF. Only in this case, the comparison > is reduced to a memcmp() between two 8-bytes arrays. > > "1234" -> 0x12, 0x34, 0xFF, 0xFF, ... > > Another small benefit of uint64_t is this is able to encode a longer > string (maybe up to 17-18 digits). That's also an advantage of keeping the data as strings, if memory allows (it'll need up to 10KB extra assuming a fixed-length slot per string). You can also add a string-length prefix for faster comparison. -- Bartc
[toc] | [prev] | [next] | [standalone]
| From | pozz <pozzugno@gmail.com> |
|---|---|
| Date | 2015-10-21 17:55 +0200 |
| Message-ID | <n08cdm$6ef$2@dont-email.me> |
| In reply to | #73469 |
Il 21/10/2015 12:27, Bartc ha scritto: > On 21/10/2015 08:29, pozz wrote: >> Il 20/10/2015 14:38, Bartc ha scritto: > >>> BCD is simpler. But you don't see the complexity of string <-> binary >>> conversion because it's inside those library functions. >> >> In my case, I will have an array of maximum 500-1000 "digit strings" >> saved in Flash memory, in compressed form. The user sometime adds and >> remove a single string, but this operation is rare, so this could be a >> little slow, even some seconds. >> The frequent operation will be searching a "digit strings" in the array, >> so comparing the reference string against each string in the array. > > So the search method is more important. > > I don't know how often you need to search, or how slow the cortex-m0 is, > but if you're using a linear search, and it's a bottleneck, you might > want to look at that. > > (With the right search method, you can keep the data as strings, and it > might still be faster than a linear search using uint64_t > >> >> If uint64_t integer is used as the encoding format of a "digit string", >> the compare is very simple, it's just a comparison between two 64-bits >> integers. On a 32-bits architecture, I think it will be a comparison >> between two 32-bits registers. I think it's very simple. >> >> If a variable length BCD string is used, I think the comparison process >> is slower. I have to compare a byte against a byte, but I have to check >> the end of the string. > > That depends. If you store the length with the BCD (as a leading byte is > simplest), and do a one-time strlen() on the reference string, then you > can quickly check the length first, using a single byte comparison. This means an array of 8-bytes can store only 14 digits (one byte is lost for length). > That can eliminate most full comparisons if the lengths are mostly > variable. But not if they are 90% the same. As it happens :-) > But, anything involving variable length entries will be harder to index > if random access is involved (eg. not using a linear search), and harder > to replace one entry with another of a different length. > >> Or I *need* to fill *all* the trailing nibbles with the terminator BCD >> tag during encoding, for example 0xF. Only in this case, the comparison >> is reduced to a memcmp() between two 8-bytes arrays. >> >> "1234" -> 0x12, 0x34, 0xFF, 0xFF, ... >> >> Another small benefit of uint64_t is this is able to encode a longer >> string (maybe up to 17-18 digits). > > That's also an advantage of keeping the data as strings, if memory > allows (it'll need up to 10KB extra assuming a fixed-length slot per > string). You can also add a string-length prefix for faster comparison. Unfortunately memory doesn't allow :-(
[toc] | [prev] | [next] | [standalone]
| From | Eric Sosman <esosman@comcast-dot-net.invalid> |
|---|---|
| Date | 2015-10-21 08:03 -0400 |
| Message-ID | <n07uqm$g1h$1@dont-email.me> |
| In reply to | #73461 |
On 10/21/2015 3:29 AM, pozz wrote:
> Il 20/10/2015 14:38, Bartc ha scritto:
>> On 20/10/2015 12:51, pozz wrote:
>>> I answer here for everyone. Thank you very much for your suggestions.
>>>
>>> Il 19/10/2015 21:11, Tim Rentsch ha scritto:
>>> > [...]
>>>> Similar idea to above, just slightly simpler implementation:
>>>>
>>>> unsigned long long
>>>> compress( const char *s ){
>>>> return strtoull( s, 0, 10 ) << 5 | strlen( s );
>>>> }
>>>>
>>>> void
>>>> decompress( char *s, unsigned long long n ){
>>>> sprintf( s, "%.*llu", (int){ n & 31 }, n >> 5 );
>>>> }
>>>>
>>>> I didn't do extensive testing but based on the little I
>>>> did I think this works for decimal strings from 0 up to 17
>>>> digits in length, including leading zeroes.
>>>
>>> I think this is the best method. I have to make some measurements, but
>>> it is sufficiently compact and simple to write and understand.
>>>
>>> The strings will be stored in a linear array and they can be added *and*
>>> removed. So I have to understand if a position is empty.
>>> With this method is sufficient to compare against zero.
>>>
>>> I have to search for a number in the list, and I'm going to process each
>>> number from the beginning to the end. The comparing between two strings
>>> will be a simple compare between two uint64_t numbers.
>>>
>>> I know uint64_t is managed by "software" functions, but I think they are
>>> highly optimized from the compiler/library supplier for my architecture.
>>> Cortex-M0 is a 32-bit MCU, so I think uint64_t numbers would be managed
>>> without complex operations.
>>>
>>> I think BCD representation is more complex without compressing much
>>> more.
>>
>> BCD is simpler. But you don't see the complexity of string <-> binary
>> conversion because it's inside those library functions.
>
> In my case, I will have an array of maximum 500-1000 "digit strings"
> saved in Flash memory, in compressed form. The user sometime adds and
> remove a single string, but this operation is rare, so this could be a
> little slow, even some seconds.
> The frequent operation will be searching a "digit strings" in the array,
> so comparing the reference string against each string in the array.
If the array changes infrequently, perhaps you can afford to keep
it in sorted order and use binary search. 9-10 comparisons per search
quite likely beats 250-500!
But, but, but: You've got 500-1000 strings of <=16 digits. You
could trivially store these as char[16] arrays (for a 16-digit string
you can omit the '\0'; you know where it belongs) for a total of
8000-16000 bytes of memory. The compression schemes discussed thus
far squash these strings into 8 bytes each, halving the memory
requirement. You can save
*** ### $$$ !!! 4000-8000 bytes !!! $$$ ### ***
(Minus whatever extra space the encoding and decoding functions need,
but that may perhaps come out of a different "budget.")
There surely exist circumstances where a savings of <8KB is worth
fighting for, but unless you're in that sort of circumstance I'd have
second thoughts about whether the effort was well-spent. And if you
*are* in such dire straits, there are fancier encodings that could
squeeze out more memory still -- at the cost of more complexity and
more code space.
But no matter what you do, the savings will be less than 16KB.
> If uint64_t integer is used as the encoding format of a "digit string",
> the compare is very simple, it's just a comparison between two 64-bits
> integers. On a 32-bits architecture, I think it will be a comparison
> between two 32-bits registers. I think it's very simple.
>
> If a variable length BCD string is used, I think the comparison process
> is slower. I have to compare a byte against a byte, but I have to check
> the end of the string.
> Or I *need* to fill *all* the trailing nibbles with the terminator BCD
> tag during encoding, for example 0xF. Only in this case, the comparison
> is reduced to a memcmp() between two 8-bytes arrays.
Why "variable-length?" You've told us the digit strings are short,
and people have proposed at least four ways to encode those shortish
strings as 64-bit numbers. Just store the encoded number as a uint64_t
and be done with it.
--
esosman@comcast-dot-net.invalid
"Don't be afraid of work. Make work afraid of you." -- TLM
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | comp.lang.c
csiph-web