herding the bits of a float into an integer array

K

Keith Thompson

It's not cast *as* an unsigned char. It's cast (i.e., explicitly
converted) *to* unsigned char. I emphasize the point because I
suspect it may be near the root of your misunderstanding.
(unsigned char)-1 is the largest possible value that can be stored in
an unsigned char.
[...]

Ok. But an unsigned char can't *have* the value negative one, right?

Right. The only values that can be stored in an object of type
unsigned char are in the range 0 .. UCHAR_MAX, where UCHAR_MAX is at
least 255 (and typically is exactly 255).

*Converting* the int value -1 to an unsigned type, because of
the way the language defines signed-to-unsigned conversions, is
a handy way of getting the maximum value of the unsigned type.
The conversion changes the mathematical value.

So even without a cast, I can write:

unsigned char uc;
uc = -1;

but the value stored in uc, after conversion, is UCHAR_MAX.
How
about ((unsigned char) -42) ?

That's UCHAR_MAX + 1 - 42 (typically 214). What about it?

If you have a copy of the standard
(<http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf> is the
latest very-nearly-official draft), take a look at the section on
conversions, particularly between signed and unsigned types.
 
F

frank

If you don't understand size_t,
then you don't understand sizeof either.

Fact is, I don't. I know things about ints. I could guess that sizeof
( S) would be nonnegative and able to be comparable to an int without
shitting the bed.
I am aware of the type and implicit conversions of every expression that
I code,
and I choose variable types accordingly.

This is a clean version of your driver with both PrintBit functions.
This is taken off of gedit after rebooting:

#include <limits.h>
#include <stdio.h>
#include <stdint.h>

#define STRING "%10f = %s\n"
#define E_TYPE float
#define INITIAL (54.3f)
#define FINAL 500
#define INC(E) ((E) *= 2)

typedef E_TYPE e_type;

void bitstr (char *str, const void *obj, size_t n);
void printFloatBits (float f);

int main (void)
{
e_type e;
char ebits[CHAR_BIT * sizeof e + 1];
puts ("\n/* BEGIN output from bitstr.c */\n");
for (e = INITIAL; FINAL >= e; INC (e))
{
bitstr (ebits, &e, sizeof e);
printf (STRING, e, ebits);
printf (" = ");
printFloatBits (e);
}
puts ("\n");
return 0;
}


void bitstr (char *str, const void *obj, size_t n)
{
unsigned mask;
const unsigned char *const byte = obj;

while (n-- != 0)
{
mask = ((unsigned char) -1 >> 1) + 1;
do
{
*str++ = (char) (mask & byte[n] ? '1' : '0');
mask >>= 1;
}
while (mask != 0);
}
*str = '\0';
}

void printFloatBits (float f)
{
uint8_t *p = ((uint8_t *) & f) + sizeof (float) - 1;

for (size_t byte = 0; byte < sizeof (float); ++byte)
{
uint8_t mask = 1 << (CHAR_BIT - 1); // Mask for MS bit
for (int bit = 0; bit < CHAR_BIT; ++bit)
{
printf ("%d", (*p & mask) > 0);
mask >>= 1;
}
--p;
}
puts ("");
}


// gcc -std=c99 -Wall -Wextra bits6.c -o out

2 questions for you, but they're both about your type qualifiers, so you
could see it as one question on two parts:

1a) Why do you declare *obj as const and void?
1b) Why the double const when assigning the value?

My second question is one that is messing with sense of reality. When I
ask a terminal to cat the above program, I get the following.

dan@dan-desktop:~/source$ cat bits6.c
#include <limits.h>
#include <stdio.h>
#include <stdint.h>

#define STRING "%10f = %s\n"
#define E_TYPE float
#define INITIAL (54.3f)
#define FINAL 500
#define INC(E) ((E) *= 2)
typedef E_TYPE e_type;
void bitstr (char *str, const void *obj, size_t n);
void printFloatBits (float f);
int main (void)
for (e = INITIAL; FINAL >= e; INC (e)) */\n");
printf (STRING, e, ebits);e);
printf (" = ");
} return 0;"); tBits (e);

void bitstr (char *str, const void *obj, size_t n)
const unsigned char *const byte = obj;
while (n-- != 0)
mask = ((unsigned char) -1 >> 1) + 1;
do
} mask >>= 1; k & byte[n] ? '1' : '0');
} while (mask != 0);
} *str = '\0';

void printFloatBits (float f)
{
uint8_t *p = ((uint8_t *) & f) + sizeof (float) - 1;

for (size_t byte = 0; byte < sizeof (float); ++byte)
{
uint8_t mask = 1 << (CHAR_BIT - 1); // Mask for MS bit
for (int bit = 0; bit < CHAR_BIT; ++bit)
{
printf ("%d", (*p & mask) > 0);
mask >>= 1;
}
--p;
}
puts ("");
}

// gcc -std=c99 -Wall -Wextra bits6.c -o out
dan@dan-desktop:~/source$

Lew Pitcher had some ideas on this, but I couldn't quite follow it and
can't tell if this looks as strange to somebody else as it does to me.
If you were playing a prank on someone, you could rewrite their cat
function, couldn't you?

The commented-out line at the end is the goocher, which I copy and paste
into the terminal, sans, of course, the first two characters. It still
compiles.
 
F

frank

It's not cast *as* an unsigned char. It's cast (i.e., explicitly
converted) *to* unsigned char. I emphasize the point because I suspect
it may be near the root of your misunderstanding.
Ok.
(unsigned char)-1 is the largest possible value that can be stored in
an unsigned char.
[...]

Ok. But an unsigned char can't *have* the value negative one, right?

Right. The only values that can be stored in an object of type unsigned
char are in the range 0 .. UCHAR_MAX, where UCHAR_MAX is at least 255
(and typically is exactly 255).

*Converting* the int value -1 to an unsigned type, because of the way
the language defines signed-to-unsigned conversions, is a handy way of
getting the maximum value of the unsigned type. The conversion changes
the mathematical value.

So even without a cast, I can write:

unsigned char uc;
uc = -1;

but the value stored in uc, after conversion, is UCHAR_MAX.
How
about ((unsigned char) -42) ?

That's UCHAR_MAX + 1 - 42 (typically 214). What about it?

So it would seem:

dan@dan-desktop:~/source$ cat temp1.c
#include <stdio.h>
int
main (void)
{
unsigned char uc;
uc = -1;
int a = (int) (uc);
printf (" %d\n", a);
uc = -42;
a = (int) (uc);
printf (" %d\n", a);
return 0;
}

// gcc -Wall -Wextra temp1.c -o temp
dan@dan-desktop:~/source$ ./temp
255
214
dan@dan-desktop:~/source$

If you were to add a flag to the open source c indent program, how would
you characterize the option not to separate int from main in the above?
If you have a copy of the standard
(<http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf> is the
latest very-nearly-official draft), take a look at the section on
conversions, particularly between signed and unsigned types.

Good, I'll take a look. Thx.
 
F

frank

If you have a copy of the standard
(<http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf> is the
latest very-nearly-official draft), take a look at the section on
conversions, particularly between signed and unsigned types.

6.3.1.3 Signed and unsigned integers
1 When a value with integer type is converted to another integer type
other than _Bool, if
the value can be represented by the new type, it is unchanged.
2 Otherwise, if the new type is unsigned, the value is converted by
repeatedly adding or
subtracting one more than the maximum value that can be represented in
the new type
until the value is in the range of the new type.49)
3 Otherwise, the new type is signed and the value cannot be represented
in it; either the
result is implementation-defined or an implementation-defined signal is
raised.

This has 2 otherwise's.
 
P

Phil Carmody

Frank said:
Why does negative one cast as an unsigned char make any sense?

Everything makes sense with unsigned chars! Negative one is the
number one less than zero, that relation is guaranteed to be
preserved by the conversion performed by the cast. It's just
good old-fashioned modular arithmetic.

Phil
 
P

Phil Carmody

frank said:
6.3.1.3 Signed and unsigned integers
1 When a value with integer type is converted to another integer type
other than _Bool, if
the value can be represented by the new type, it is unchanged.
2 Otherwise, if the new type is unsigned, the value is converted by
repeatedly adding or
subtracting one more than the maximum value that can be represented in
the new type
until the value is in the range of the new type.49)
3 Otherwise, the new type is signed and the value cannot be represented
in it; either the
result is implementation-defined or an implementation-defined signal is
raised.

This has 2 otherwise's.

If you're interested in what the upcoming version of the standard
says about it, read the draft version of the standard, otherwise
read the current standard with its technical corrigenda.

That has an otherwise in it.

If you have a programming question which is more concerned with
the algorithm to use rather than the C syntax necessary to achieve
that algorithm, then post to comp.programming, otherwise post to
comp.lang.c.

That also has an otherwise in it.

So what? Do you presume that if and else don't exist?

Phil
 
K

Keith Thompson

frank said:
6.3.1.3 Signed and unsigned integers
1 When a value with integer type is converted to another integer type
other than _Bool, if
the value can be represented by the new type, it is unchanged.
2 Otherwise, if the new type is unsigned, the value is converted by
repeatedly adding or
subtracting one more than the maximum value that can be represented in
the new type
until the value is in the range of the new type.49)
3 Otherwise, the new type is signed and the value cannot be represented
in it; either the
result is implementation-defined or an implementation-defined signal is
raised.

This has 2 otherwise's.

Yes, and 12 "the"s, 8 "is"s, 7 "type"s, 6 "value"s, and a bunch of
other words.

Did you have a question?

My point: I can guess that your statement that "This has 2
otherwise's" is intended to indicate that you're having trouble
understanding the quoted text, but that's only a guess. If you
have a question about it, you're going to have to be much more
specific. If you have some other reason to remark on the number
of "otherwise"s, then again, you're going to have to be much more
specific.
 
L

lawrence.jones

frank said:
6.3.1.3 Signed and unsigned integers
1 When a value with integer type is converted to another integer type
other than _Bool, if
the value can be represented by the new type, it is unchanged.
2 Otherwise, if the new type is unsigned, the value is converted by
repeatedly adding or
subtracting one more than the maximum value that can be represented in
the new type
until the value is in the range of the new type.49)
3 Otherwise, the new type is signed and the value cannot be represented
in it; either the
result is implementation-defined or an implementation-defined signal is
raised.

This has 2 otherwise's.

It has an "if", followed by an "otherwise, if", followed by a plain
"otherwise" that applies when neither of the "if"s is true.
 
F

Frank

((size_t)-1), is situation 2.

The new type is unsigned.

I have no C capability, unless it's to announce Christmas.

The otherwise's were never wise men. Someone's gonna have to be my crash
test dummy as I hit Zion. I hope it's Stan and that he has horses to ride.

Is the mapping onto?
 
K

Keith Thompson

Frank said:
frank wrote: [...]
This has 2 otherwise's.

((size_t)-1), is situation 2.

The new type is unsigned.

I have no C capability, unless it's to announce Christmas.

The otherwise's were never wise men. Someone's gonna have to be my crash
test dummy as I hit Zion. I hope it's Stan and that he has horses to ride.

Is the mapping onto?

You really should make sure you're sober before posting here.
 
F

Frank

You really should make sure you're sober before posting here.

That'd be a little unfun if taken strictly, but when a guy with a degree in
math gets "onto" wrong, he has had too much fun while out singing. I'll
say "mea minima culpa," because it's the season.

limits.h contains the domain parameters of the integer datatypes. When you
map them onto a smaller set, that is, convert them in the sense of §6.3,
does every element in the domain find a home in the range, which is the
smaller entity in which we're doing the modular arithmetic?

Onto is something different and not hard to show because I think it's
sufficient to say that they're subsets, and these representations are
required to be what I would call "tochno."
 
B

Ben Bacarisse

Frank said:
That'd be a little unfun if taken strictly, but when a guy with a degree in
math gets "onto" wrong, he has had too much fun while out singing. I'll
say "mea minima culpa," because it's the season.

I doubt the phrase you quoted is what prompted Keith's reply.
limits.h contains the domain parameters of the integer datatypes. When you
map them onto a smaller set, that is, convert them in the sense of §6.3,
does every element in the domain find a home in the range, which is the
smaller entity in which we're doing the modular arithmetic?

6.3 defines mappings between sets, only some of which are to smaller
sets. For example, for most signed T to unsigned T conversions, the
sets have the same size in most implementations.

Your sentence ends with a ? but I can't give an answer unless you
explain what a "home" is for a mapped element. 6.3 defines
conversions that are sometimes undefined. It also defines conversions
that are always well defined. That may answer your question.
Onto is something different and not hard to show because I think it's
sufficient to say that they're subsets, and these representations are
required to be what I would call "tochno."

I'd ask what that means but I won't because I am not sure you have
taken Keith's advice.
 
B

Ben Bacarisse

Frank said:
This "if" is supposed to stand before both 2 and 3?

It is before 2 and 3. Does "stand before" mean anything more that
"come before"?

If you are asking if the two "otherwise" clauses progressively exhaust
the space of conversions from signed to unsigned integers, I'd say that
that must be the meaning. If it is not, what could be the meaning of
either the first or the second "otherwise"?

<snip>
 
L

lawrence.jones

Frank said:
This "if" is supposed to stand before both 2 and 3?

The structure is:

if (...) {...}
else {
if (...) {...}
else {...}
}

which many people find clearer when written (as the English above is)
as:

if (...) {...}
else if (...) {...}
else {...}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,117
Latest member
Matilda564
Top