Discussion:
Negative zero representation in an unsigned type
(too old to reply)
Keith Thompson
2016-10-15 21:43:43 UTC
Permalink
Raw Message
N1570 6.2.5p9 says:

The range of nonnegative values of a signed integer type is a
subrange of the corresponding unsigned integer type, and the
representation of the same value in each type is the same.

with a footnote (#41 in N1570):

The same representation and alignment requirements are meant
to imply interchangeability as arguments to functions, return
values from functions, and members of unions.

This is straightforward for most systems; the values from 0 to
INT_MAX can be represented either as signed int or as unsigned int,
and have the same representation in both. (The same applies to
other corresponding pairs of signed and unsigned types, but I'll
refer to signed/unsigned int for simplicity.)

But it overlooks the possibility of a value having more than
one representation. It refers to "*the* representation", but
6.2.6.1p4 says:

Two values (other than NaNs) with the same object representation
compare equal, but values that compare equal may have different
object representations.

I think the intent is that if a value within the range 0..INT_MAX has
more than one representation, then both are representions of that
value for both signed int and unsigned int. Otherwise, the intent
expressed in footnote 41 would be difficult or impossible to support.

So far so good -- but what about negative zero in ones' complement
and sign and magnitude systems?

6.2.6.2p2:

Which of these [the three schemes for representing negative
integers] applies is implementation-defined, as is whether
the value with sign bit 1 and all value bits zero (for the
first two), or with sign bit and all value bits 1 (for ones’
complement), is a trap representation or a normal value. In
the case of sign and magnitude and ones’ complement, if this
representation is a normal value it is called a *negative zero*.

So here's the question. For a non-two's-complement system with
negative zero as a normal value, is it required that storing the
representation of negative zero in an unsigned object results in
that object having the value zero?

6.2.5p9 seems to imply that the answer is yes, but practicality
suggests that it's no. Assuming unsigned int has no padding bits and
therefore no trap representations, then representations corresponding
to zero and to negative zero must have different value.

I think the most reasonable resolution is to say that 6.2.5p9
doesn't apply to negative zero.

I'm assuming that zero and negative zero are the same value, and
more generally that two values that compare equal are the same value.
--
Keith Thompson (The_Other_Keith) kst-***@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"
Tim Rentsch
2016-10-16 00:23:36 UTC
Permalink
Raw Message
Post by Keith Thompson
The range of nonnegative values of a signed integer type is a
subrange of the corresponding unsigned integer type, and the
representation of the same value in each type is the same.
The same representation and alignment requirements are meant
to imply interchangeability as arguments to functions, return
values from functions, and members of unions.
This is straightforward for most systems; the values from 0 to
INT_MAX can be represented either as signed int or as unsigned int,
and have the same representation in both. (The same applies to
other corresponding pairs of signed and unsigned types, but I'll
refer to signed/unsigned int for simplicity.)
But it overlooks the possibility of a value having more than
one representation. It refers to "*the* representation", but
Two values (other than NaNs) with the same object representation
compare equal, but values that compare equal may have different
object representations.
I think the intent is that if a value within the range 0..INT_MAX has
more than one representation, then both are representions of that
value for both signed int and unsigned int. Otherwise, the intent
expressed in footnote 41 would be difficult or impossible to support.
So far so good -- but what about negative zero in ones' complement
and sign and magnitude systems?
Which of these [the three schemes for representing negative
integers] applies is implementation-defined, as is whether
the value with sign bit 1 and all value bits zero (for the
first two), or with sign bit and all value bits 1 (for ones?
complement), is a trap representation or a normal value. In
the case of sign and magnitude and ones? complement, if this
representation is a normal value it is called a *negative zero*.
So here's the question. For a non-two's-complement system with
negative zero as a normal value, is it required that storing the
representation of negative zero in an unsigned object results in
that object having the value zero?
6.2.5p9 seems to imply that the answer is yes, but practicality
suggests that it's no. Assuming unsigned int has no padding bits and
therefore no trap representations, then representations corresponding
to zero and to negative zero must have different value.
I think the most reasonable resolution is to say that 6.2.5p9
doesn't apply to negative zero.
I'm assuming that zero and negative zero are the same value, and
more generally that two values that compare equal are the same value.
For what it's worth, I resolve this problem by treating negative
zero as "negative" for the purposes of 6.2.5p9, which I believe
is what was intended. Regular zero and negative zero have the
same value, but different signedness, so only regular zeros
get consideration as far as 6.2.5p9 is concerned.

I admit I have no direct evidence to support this view. But it
seems like the best fit to what there is in the way of indirect
or circumstantial evidence, so until a better explanation comes
along I will stick with this one.
Nick Bowler
2016-10-19 16:08:52 UTC
Permalink
Raw Message
Post by Keith Thompson
I'm assuming that zero and negative zero are the same value, and
more generally that two values that compare equal are the same value.
I don't think this assumption is supported by the standard.

Negative zero and zero are different values with different object
representations. Maybe it's more obvious if you consider floating
point (on implementations supporting negative zeros and infinities,
which is typical): here negative zero and non-negative zero still
compare equal but are definitely not the same value:

0. == -0. -> 1
1/0. != 1/-0. -> 1

Back to integers: since only non-negative zero is representable in
unsigned types, the interchangeability requirement applies only to
that one.
James R. Kuyper
2016-10-19 16:44:21 UTC
Permalink
Raw Message
Post by Nick Bowler
Post by Keith Thompson
I'm assuming that zero and negative zero are the same value, and
more generally that two values that compare equal are the same value.
I don't think this assumption is supported by the standard.
Negative zero and zero are different values with different object
representations. Maybe it's more obvious if you consider floating
point (on implementations supporting negative zeros and infinities,
which is typical): here negative zero and non-negative zero still
0. == -0. -> 1
1/0. != 1/-0. -> 1
IEC 60559 specifies different behavior in many contexts for negative
zero and positive zero. The C standard never says that negative zeros of
integer type can behave differently from positive ones, except that
expressions involving "the +, -, *, /, and % operators where one operand
is a negative zero and the result is zero;" can generate that result as
a negative zero (6.2.6.2p3). Note that it says "the result is zero",
while at the same time it says a negative zero may be generated, which
is consistent with the idea that a negative zero is also just plain zero.

Let's consider a ones' complement signed char on an implementation that
has CHAR_BIT==8 and which supports negative zero for that type. The
standard defines what "ones' complement" means in the context of the C
standard entirely by specifying in 6.2.6.2p2 that the sign bit has a
value of -(2^M-1), where M==7 for this case, so the sign bit has a value
of -127. "one's complement" is italicized, which is an ISO convention
indicating that this fact constitutes the entire official definition of
that term, as far as the C standard is concerned.

I cannot find any clause in the standard which explicitly says that the
value represented by an integer object is the sum of the values of the
bits that are set in it's representation. It explicitly says otherwise
in 6.2.6.2p2 for sign and magnitude representations. However for the
other two permitted representations of signed integers, I believe that
this precisely the intended implication of what it says about the values
of the value bits in p1, about sign bits in p2, and also what it refuses
to specify about the value of padding bits in p5. If you wish to dispute
this assumption, I can't object. However, you'd better be prepared to
identify a conflicting specification in the standard of the connection
between the values of the individual bits and the value that the entire
object represents.

When the bits of a such a signed char are 00000000, none of the bits are
set, so the sum of the values of the bits that are set is 0. When the
bits are 11111111, then the sum of the values of the bits that are set
is -127 + 64 + 32 + 16 + 8 + 4 + 2 + 1, which is also 0. I don't see in
that process any basis you can use for claiming that those
representations represent different values.
s***@casperkitty.com
2016-10-19 17:25:54 UTC
Permalink
Raw Message
Post by James R. Kuyper
IEC 60559 specifies different behavior in many contexts for negative
zero and positive zero. The C standard never says that negative zeros of
integer type can behave differently from positive ones, except that
expressions involving "the +, -, *, /, and % operators where one operand
is a negative zero and the result is zero;" can generate that result as
a negative zero (6.2.6.2p3). Note that it says "the result is zero",
while at the same time it says a negative zero may be generated, which
is consistent with the idea that a negative zero is also just plain zero.
That would imply that storing an integer with the value 1-1 must yield a
bit pattern that, when read as unsigned, would yield 0. I think the
effects of the rule quoted above may have been a little broader than
intended, however. Allowing a compiler to interpret expressions like
x+0, 0+x, x-0, etc. as simply x, without regard for whether x might be
negative zero, is helpful; I see less benefit and more downsides to
allowing an expression like (x-x) or x*0 to behave likewise in cases
where x is known to be either zero or negative zero.

In any case, given the above rule I think it would be reasonable to say
that negative zero is considered to be outside the range of values that
are representable as both "int" and "unsigned", and that in cases where
an int could legitimately hold negative zero, reading those bits as
unsigned would yield UB, but that in cases where the value would either
need to be a positive number or "ordinary" zero reading the bits as
unsigned would preserve the numerical value represented thereby.
Post by James R. Kuyper
Let's consider a ones' complement signed char on an implementation that
When the bits of a such a signed char are 00000000, none of the bits are
set, so the sum of the values of the bits that are set is 0. When the
bits are 11111111, then the sum of the values of the bits that are set
is -127 + 64 + 32 + 16 + 8 + 4 + 2 + 1, which is also 0. I don't see in
that process any basis you can use for claiming that those
representations represent different values.
An implementation running on hardware where e.g. subtracting one non-zero
negative number from itself could yield negative zero would be required to
add additional code to ensure that such a result gets turned into positive
zero, either via code equivalent to:

x=y+z; if (x==0) x=0;

or perhaps, on an implementation with silent wraparound behavior,

x=((y+z)+1)-1;

Negative zero is a special value which some operators treat like zero, but
which can be distinguished from zero using other operators. It's a bit like
NaN, but with less-goofy rules for comparisons.
Keith Thompson
2016-10-19 18:30:56 UTC
Permalink
Raw Message
Post by s***@casperkitty.com
Post by James R. Kuyper
IEC 60559 specifies different behavior in many contexts for negative
zero and positive zero. The C standard never says that negative zeros of
integer type can behave differently from positive ones, except that
expressions involving "the +, -, *, /, and % operators where one operand
is a negative zero and the result is zero;" can generate that result as
a negative zero (6.2.6.2p3). Note that it says "the result is zero",
while at the same time it says a negative zero may be generated, which
is consistent with the idea that a negative zero is also just plain zero.
That would imply that storing an integer with the value 1-1 must yield a
bit pattern that, when read as unsigned, would yield 0.
Yes, the result of the expression 1-1 cannot be a negative zero.

The bitwise operators &, |, ^, ~, <<, and >> can create a negative zero
from "normal" operands (where by "normal" I mean anything other than a
negative zero). The arithmetic operators +, -, *, /, and % can only
yield a negative zero given a negative zero operand.

This might require a little extra work for some implementation.

[...]
--
Keith Thompson (The_Other_Keith) kst-***@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"
Keith Thompson
2016-10-19 17:00:20 UTC
Permalink
Raw Message
Post by Nick Bowler
Post by Keith Thompson
I'm assuming that zero and negative zero are the same value, and
more generally that two values that compare equal are the same value.
I don't think this assumption is supported by the standard.
Perhaps -- but I'm not 100% convinced it's contradicted by the
standard either, though I'm now leaning that way.
Post by Nick Bowler
Negative zero and zero are different values with different object
representations. Maybe it's more obvious if you consider floating
point (on implementations supporting negative zeros and infinities,
which is typical): here negative zero and non-negative zero still
0. == -0. -> 1
1/0. != 1/-0. -> 1
Back to integers: since only non-negative zero is representable in
unsigned types, the interchangeability requirement applies only to
that one.
Floating-point raises another issue: NAN and NAN (for an implementation
that defines it) are clearly the same value for any reasonable meaning
of "same" and "value", but NAN != NAN.

The standard defines "value" as
precise meaning of the contents of an object when interpreted as
having a specific type
and I suppose 0 and -0 have a different "precise meaning". (I'm using
"-0" is a shorthand for negative zero; applying unary "-" to 0 doesn't
actually yield negative zero.)

Ironically, the problem here seems to be determining the precise meaning
of "precise meaning".

Here are my tentative conclusions so far (for an implementation that
supports an integer negative zero):

- Zero and negative zero compare equal, but are distinct values.

- Negative zero is not a "nonnegative value" as the term is used in
6.2.6.2p9.

- A signed integer object holding negative zero, when treated as an
object of the corresponding unsigned integer type, does not
necessarily yield either a value of 0 or a value equal to 0.

- The fact that NAN != NAN demonstrates that x==y does *not* simply mean
that x and y have the same value. Given that, it's reasonable that
i==j is true where i is 0 and z is negative zero, even though i and j
hold different values.

- This can probably be inferred from the wording in the standard, but a
little more clarity would be welcome.

Unless I've missed something (which is always a real possibility), the
standard doesn't actually define what "==" means for integer or
floating-point operands. It's defined in some detail for pointer
operands, but for integer and floating-point operands the standard just
says it "yields 1 if the specified relation is true and 0 if it is
false".

What does "==" actually mean, and can that meaning be expressed clearly,
accurately, and briefly?

In the implementation I'm using, the expression -0.0 yields a value
(a floating-point negative zero) whose representation differs from
that of 0.0. The expression 0.0 == -0.0 is true. Is that required,
or could a conforming implementation have "==" yield 0?

For an implementation that supports integer negative zero, is
negative zero required to compare equal to 0 (using the "=="
operator)? If so, where is this specified?
--
Keith Thompson (The_Other_Keith) kst-***@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"
Nick Bowler
2016-10-19 17:33:08 UTC
Permalink
Raw Message
Post by Keith Thompson
Unless I've missed something (which is always a real possibility), the
standard doesn't actually define what "==" means for integer or
floating-point operands. It's defined in some detail for pointer
operands, but for integer and floating-point operands the standard just
says it "yields 1 if the specified relation is true and 0 if it is
false".
For all of the relational and equality operators, the "specified
relation[s]" are explicitly named: "less than", "greater than",
"less than or equal to", "greater than or equal to", "equal to"
and "not equal to".

I suspect that one of the standard's normative references covers
the meanings of these, perhaps either ISO 31–11:1992 "Quantities and
units — Part 11: Mathematical signs and symbols for use in the physical
sciences and technology." or ISO/IEC 2382-1:1993, "Information
technology — Vocabulary — Part 1: Fundamental terms."
s***@casperkitty.com
2016-10-19 17:45:19 UTC
Permalink
Raw Message
Post by Nick Bowler
For all of the relational and equality operators, the "specified
relation[s]" are explicitly named: "less than", "greater than",
"less than or equal to", "greater than or equal to", "equal to"
and "not equal to".
I suspect that one of the standard's normative references covers
the meanings of these, perhaps either ISO 31–11:1992 "Quantities and
units — Part 11: Mathematical signs and symbols for use in the physical
sciences and technology." or ISO/IEC 2382-1:1993, "Information
technology — Vocabulary — Part 1: Fundamental terms."
With regard to mathematical real numbers, the "equals" and "not-equals"
operators define an equivalence relation, while the other operators
listed define a transitive ordering. The C Standard does not require
that its operators behave in such fashion when applied to floating-point
quantities; indeed, implementations that support Annex F are required to
implement those operators in a fashion inconsistent with the mathematics
of real numbers.
Keith Thompson
2016-10-19 18:41:18 UTC
Permalink
Raw Message
Post by s***@casperkitty.com
Post by Nick Bowler
For all of the relational and equality operators, the "specified
relation[s]" are explicitly named: "less than", "greater than",
"less than or equal to", "greater than or equal to", "equal to"
and "not equal to".
I suspect that one of the standard's normative references covers
the meanings of these, perhaps either ISO 31–11:1992 "Quantities and
units — Part 11: Mathematical signs and symbols for use in the physical
sciences and technology." or ISO/IEC 2382-1:1993, "Information
technology — Vocabulary — Part 1: Fundamental terms."
With regard to mathematical real numbers, the "equals" and "not-equals"
operators define an equivalence relation, while the other operators
listed define a transitive ordering. The C Standard does not require
that its operators behave in such fashion when applied to floating-point
quantities; indeed, implementations that support Annex F are required to
implement those operators in a fashion inconsistent with the mathematics
of real numbers.
Not all floating-point values are real numbers. The operators
behave in a fashion consistent with the mathematics of real numbers
for those floating-point values that correspond to real numbers.
--
Keith Thompson (The_Other_Keith) kst-***@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"
Kaz Kylheku
2016-10-19 18:54:44 UTC
Permalink
Raw Message
Post by Keith Thompson
Post by s***@casperkitty.com
Post by Nick Bowler
For all of the relational and equality operators, the "specified
relation[s]" are explicitly named: "less than", "greater than",
"less than or equal to", "greater than or equal to", "equal to"
and "not equal to".
I suspect that one of the standard's normative references covers
the meanings of these, perhaps either ISO 31–11:1992 "Quantities and
units — Part 11: Mathematical signs and symbols for use in the physical
sciences and technology." or ISO/IEC 2382-1:1993, "Information
technology — Vocabulary — Part 1: Fundamental terms."
With regard to mathematical real numbers, the "equals" and "not-equals"
operators define an equivalence relation, while the other operators
listed define a transitive ordering. The C Standard does not require
that its operators behave in such fashion when applied to floating-point
quantities; indeed, implementations that support Annex F are required to
implement those operators in a fashion inconsistent with the mathematics
of real numbers.
Not all floating-point values are real numbers. The operators
behave in a fashion consistent with the mathematics of real numbers
for those floating-point values that correspond to real numbers.
Bingo. Though for instance the = operator may be anti-reflexive
when applied to NaN, it is symmetric, reflexive and transitive on
numbers. Ergo, over that domain, it defines an equivalence relation,
partitiong it into equivalence classes. Similarly, there are no
counterexamples among floats that represent numbers to the transitivity
that A <= B <= C; that it implies A <= C.

In any case, why complain? Basically, that Annex F stuff is support for
a platform! It's doing exactly what supercat wants the standard to do:
modeling language semantics after a common fashion in which mainstream
hardware works. Common floating hardware obeys IEEE nowadays, and so, by
golly, this Annex brings that into the language, at least optionally,
features so that numeric code which is more subtly nuanced than what
was possible before within the bounds of ISO C.
Keith Thompson
2016-10-19 19:04:39 UTC
Permalink
Raw Message
[...]
Post by Kaz Kylheku
Post by Keith Thompson
Not all floating-point values are real numbers. The operators
behave in a fashion consistent with the mathematics of real numbers
for those floating-point values that correspond to real numbers.
Bingo. Though for instance the = operator may be anti-reflexive
when applied to NaN, it is symmetric, reflexive and transitive on
numbers.
Obviously you mean ==, not =.
Post by Kaz Kylheku
Ergo, over that domain, it defines an equivalence relation,
partitiong it into equivalence classes. Similarly, there are no
counterexamples among floats that represent numbers to the transitivity
that A <= B <= C; that it implies A <= C.
Agreed. (Insert an equally obvious minor quibble about operator
precedence.)

[...]
--
Keith Thompson (The_Other_Keith) kst-***@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"
s***@casperkitty.com
2016-10-19 19:20:25 UTC
Permalink
Raw Message
On Wednesday, October 19, 2016 at 1:54:47 PM UTC-5, Kaz Kylheku wrote:> In any case, why complain? Basically, that Annex F stuff is support for
Post by Kaz Kylheku
modeling language semantics after a common fashion in which mainstream
hardware works. Common floating hardware obeys IEEE nowadays, and so, by
golly, this Annex brings that into the language, at least optionally,
features so that numeric code which is more subtly nuanced than what
was possible before within the bounds of ISO C.
Annex F is the kind of thing the Standard should do a lot more of.
While restricting arguments to numbers would make `==` an equivalence
relation when applied directly to variables or constants of floating-
point types, some other implications associated with mathematical
equivalence do not hold. Mathematically, and in many non-IEEE-754
floating-point implementations, for example, f==g would imply that
anything which is true of f is true of g. Annex F requires that f==g
report true in some cases (e.g. positive and negative zero) where that
is not the case. Such behavior can cause trouble if e.g. code assumes
that if it knows that f(x) is a pure function on x, f(x1)==y and
x1==x2, it can safely replace f(x2) with y. That would be true on
many non-IEEE-754 floating-point implementations but can fail if x and
y are positive and negative zero.
Keith Thompson
2016-10-19 18:39:57 UTC
Permalink
Raw Message
Post by Nick Bowler
Post by Keith Thompson
Unless I've missed something (which is always a real possibility), the
standard doesn't actually define what "==" means for integer or
floating-point operands. It's defined in some detail for pointer
operands, but for integer and floating-point operands the standard just
says it "yields 1 if the specified relation is true and 0 if it is
false".
For all of the relational and equality operators, the "specified
relation[s]" are explicitly named: "less than", "greater than",
"less than or equal to", "greater than or equal to", "equal to"
and "not equal to".
I suspect that one of the standard's normative references covers
the meanings of these, perhaps either ISO 31–11:1992 "Quantities and
units — Part 11: Mathematical signs and symbols for use in the physical
sciences and technology." or ISO/IEC 2382-1:1993, "Information
technology — Vocabulary — Part 1: Fundamental terms."
Both have been superseded. The first is superseded by ISO 80000-2:2009,
which apparently costs 158 CHF (currently about $160 US). There's a
Wikipedia article about ISO 31-11, but it just says that "a = b" means
"a equals b". I don't expect the actual standard to be much more
informative than that, but I'm not going to spend the money to find out.

The second is revised by ISO/IEC 2382:2015, which is available online
but doesn't seem to help answer this question.

https://en.wikipedia.org/wiki/ISO_31-11
http://www.iso.org/iso/catalogue_detail?csnumber=31887

http://www.iso.org/iso/home/store/catalogue_ics/catalogue_detail_ics.htm?csnumber=63598
https://www.iso.org/obp/ui/#iso:std:iso-iec:2382:ed-1:v1:en
--
Keith Thompson (The_Other_Keith) kst-***@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"
Tim Rentsch
2016-10-19 22:37:05 UTC
Permalink
Raw Message
Post by Keith Thompson
Post by Nick Bowler
Post by Keith Thompson
I'm assuming that zero and negative zero are the same value, and
more generally that two values that compare equal are the same value.
I don't think this assumption is supported by the standard.
Perhaps -- but I'm not 100% convinced it's contradicted by the
standard either, though I'm now leaning that way.
[...]
Here are my tentative conclusions so far (for an implementation that
- Zero and negative zero compare equal, but are distinct values.
- Negative zero is not a "nonnegative value" as the term is used in
6.2.6.2p9.
- A signed integer object holding negative zero, when treated as an
object of the corresponding unsigned integer type, does not
necessarily yield either a value of 0 or a value equal to 0.
I agree with all of these.
Post by Keith Thompson
- The fact that NAN != NAN demonstrates that x==y does *not* simply mean
that x and y have the same value.
What == means depends on the algebra of the abstract value space.
The abstract value space of floating point numbers can include
values that do not compare == to any other value in the space,
include the same abstract value.
Post by Keith Thompson
Given that, it's reasonable that
i==j is true where i is 0 and z is negative zero, even though i and j
hold different values.
I don't think one follows from the other. The algebra of
integer abstract values is different from the algebra of
floating point abstract values, that's all.
Post by Keith Thompson
- This can probably be inferred from the wording in the standard, but a
little more clarity would be welcome.
A little more clarity would be welcome. A lot more clarity would
be even more welcome.
Post by Keith Thompson
Unless I've missed something (which is always a real possibility),
the standard doesn't actually define what "==" means for integer or
floating-point operands. It's defined in some detail for pointer
operands, but for integer and floating-point operands the standard
just says it "yields 1 if the specified relation is true and 0 if it
is false".
I would say that the Standard does define what "==" means,
but it doesn't define it in much detail, and certainly not
as much detail as I would like. This case is one where I
believe the Standard does appeal to "common sense", to its
detriment.
Post by Keith Thompson
What does "==" actually mean, and can that meaning be expressed
clearly, accurately, and briefly?
For integers, it means the value bits and sign bit are mapped,
in the obvious way, onto an abstract integer, and the abstract
integer values are compared to see if they are equal.

Floating point is basically the same, except the mapping isn't
nearly as obvious, and there may be additional cases like
positive infinity and negative infinity (which I think compare
equal to themselves and not to anything else) and NaN's, which
don't compare equal to anything. Oh, and let's not forget about
signalling NaN's, which give the answer "you can't get there from
here."

So there's a brief description, which is one-third the way
of where you wanted to get. ;)
Post by Keith Thompson
In the implementation I'm using, the expression -0.0 yields a value
(a floating-point negative zero) whose representation differs from
that of 0.0. The expression 0.0 == -0.0 is true. Is that required,
or could a conforming implementation have "==" yield 0?
I believe the intention is that this result is required, yes.
Post by Keith Thompson
For an implementation that supports integer negative zero, is
negative zero required to compare equal to 0 (using the "=="
operator)? If so, where is this specified?
I believe it is required. The requirement is indirectly
specified (a bit too indirectly to my tastes) in the first few
paragraphs of section 6.2.6.2.
s***@casperkitty.com
2016-10-19 23:21:28 UTC
Permalink
Raw Message
Post by Keith Thompson
For an implementation that supports integer negative zero, is
negative zero required to compare equal to 0 (using the "=="
operator)? If so, where is this specified?
For each form of integer representation, there is one bit pattern which the
Standard would allow implementations to handle arithmetic in any fashion
whatsoever.

For two's-complement representations, that value is ~INT_MAX, for sign-
magnitude implementations it's INT_MIN^INT_MAX, and for ones'-complement
implementations, it's ~0.

The Standard defines a means via which a program can test whether it is
running on a two's-complement implementation that promises not to do
anything weird with the aforementioned bit pattern: it can test whether
INT_MIN < -INT_MAX. Is there any means via which the Standard would
allow a program to do likewise on a sign-magnitude or ones'-complement
implementation? If not, would the Standard impose any requirements on
code which performs arithmetic on "negative zero"?
Keith Thompson
2016-10-19 23:38:42 UTC
Permalink
Raw Message
Post by s***@casperkitty.com
Post by Keith Thompson
For an implementation that supports integer negative zero, is
negative zero required to compare equal to 0 (using the "=="
operator)? If so, where is this specified?
Please quote and cite properly. I wrote the above paragraph. You're
replying to Tim's followup, but you haven't quoted anything he wrote.
Post by s***@casperkitty.com
For each form of integer representation, there is one bit pattern which the
Standard would allow implementations to handle arithmetic in any fashion
whatsoever.
N1570 6.2.6.2p2. The value with sign bit 1 and all other bits 0 (for
sign-and-magnitude and two's-complement), or with sign bit and all value
bits 1 (for ones'-complement) may be a trap representation; if so, it's
irrelevant to what I said, since I asked only about implementations that
support negative zero. Or that value can be a normal value, called a
*negative zero* for sign-and-magnitude or ones'-complement. (There is
no negative zero for a two's-complement implementation. The choice
(trap representation vs. normal value) is implementation-defined, so it
must be documented.

[...]
--
Keith Thompson (The_Other_Keith) kst-***@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"
s***@casperkitty.com
2016-10-20 03:48:42 UTC
Permalink
Raw Message
Post by Keith Thompson
N1570 6.2.6.2p2. The value with sign bit 1 and all other bits 0 (for
sign-and-magnitude and two's-complement), or with sign bit and all value
bits 1 (for ones'-complement) may be a trap representation; if so, it's
irrelevant to what I said, since I asked only about implementations that
support negative zero. Or that value can be a normal value, called a
*negative zero* for sign-and-magnitude or ones'-complement. (There is
no negative zero for a two's-complement implementation. The choice
(trap representation vs. normal value) is implementation-defined, so it
must be documented.
Does anything in the Standard specify what form such documentation must
take? Would any conforming implementation become non-conforming if all
references to the actual behavior were stricken and replaced by a statement
that the bit pattern is a trap representation whose behavior may be subject
to change without notice?
Keith Thompson
2016-10-20 15:28:50 UTC
Permalink
Raw Message
Post by s***@casperkitty.com
Post by Keith Thompson
N1570 6.2.6.2p2. The value with sign bit 1 and all other bits 0 (for
sign-and-magnitude and two's-complement), or with sign bit and all value
bits 1 (for ones'-complement) may be a trap representation; if so, it's
irrelevant to what I said, since I asked only about implementations that
support negative zero. Or that value can be a normal value, called a
*negative zero* for sign-and-magnitude or ones'-complement. (There is
no negative zero for a two's-complement implementation. The choice
(trap representation vs. normal value) is implementation-defined, so it
must be documented.
Does anything in the Standard specify what form such documentation must
take? Would any conforming implementation become non-conforming if all
references to the actual behavior were stricken and replaced by a statement
that the bit pattern is a trap representation whose behavior may be subject
to change without notice?
I'm sure we can all count on you to let us know if that ever becomes an
actual issue. Until then, I'll just assume that implementations will
document that particular choice in a reasonable manner.
--
Keith Thompson (The_Other_Keith) kst-***@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"
Tim Rentsch
2016-10-20 16:12:06 UTC
Permalink
Raw Message
Post by s***@casperkitty.com
Post by Keith Thompson
For an implementation that supports integer negative zero, is
negative zero required to compare equal to 0 (using the "=="
operator)? If so, where is this specified?
For each form of integer representation, there is one bit pattern which the
Standard would allow implementations to handle arithmetic in any fashion
whatsoever.
For two's-complement representations, that value is ~INT_MAX, for sign-
magnitude implementations it's INT_MIN^INT_MAX, and for ones'-complement
implementations, it's ~0.
The Standard defines a means via which a program can test whether it is
running on a two's-complement implementation that promises not to do
anything weird with the aforementioned bit pattern: it can test whether
INT_MIN < -INT_MAX. Is there any means via which the Standard would
allow a program to do likewise on a sign-magnitude or ones'-complement
implementation? If not, would the Standard impose any requirements on
code which performs arithmetic on "negative zero"?
These seem like pointless or rhetorical questions. And still too
wordy. Possible re-write:

"Is there a way a program can tell if the implementation supports
negative zeros?"

This replaces 118 words by 14 words, and has basically the same
content.
Jakob Bohm
2016-10-23 19:07:58 UTC
Permalink
Raw Message
Post by Tim Rentsch
Post by s***@casperkitty.com
Post by Keith Thompson
For an implementation that supports integer negative zero, is
negative zero required to compare equal to 0 (using the "=="
operator)? If so, where is this specified?
For each form of integer representation, there is one bit pattern which the
Standard would allow implementations to handle arithmetic in any fashion
whatsoever.
For two's-complement representations, that value is ~INT_MAX, for sign-
magnitude implementations it's INT_MIN^INT_MAX, and for ones'-complement
implementations, it's ~0.
The Standard defines a means via which a program can test whether it is
running on a two's-complement implementation that promises not to do
anything weird with the aforementioned bit pattern: it can test whether
INT_MIN < -INT_MAX. Is there any means via which the Standard would
allow a program to do likewise on a sign-magnitude or ones'-complement
implementation? If not, would the Standard impose any requirements on
code which performs arithmetic on "negative zero"?
These seem like pointless or rhetorical questions. And still too
"Is there a way a program can tell if the implementation supports
negative zeros?"
This replaces 118 words by 14 words, and has basically the same
content.
Not quite, it replaces 3 interesting questions with one much less
interesting one.

There are many cases where a program may care if the implementation
uses sign-magnitude representation or not. It would thus be useful for
such a test to exist within the limited scope of strictly conforming
programs.

There are many cases where a program may care if the implementation
uses ones-complement or not. It would thus be useful for such a test
to exist within the limited scope of strictly conforming programs.

Finally, the possible absence of both the above useful language
abilities might or might not raise a logical question as to the absence
of information on the handling of negative zero in the standard.

Enjoy

Jakob
--
Jakob Bohm, CIO, Partner, WiseMo A/S. https://www.wisemo.com
Transformervej 29, 2860 Søborg, Denmark. Direct +45 31 13 16 10
This public discussion message is non-binding and may contain errors.
WiseMo - Remote Service Management for PCs, Phones and Embedded
Keith Thompson
2016-10-23 21:10:22 UTC
Permalink
Raw Message
[...]
Post by Jakob Bohm
Post by Tim Rentsch
These seem like pointless or rhetorical questions. And still too
"Is there a way a program can tell if the implementation supports
negative zeros?"
This replaces 118 words by 14 words, and has basically the same
content.
Not quite, it replaces 3 interesting questions with one much less
interesting one.
There are many cases where a program may care if the implementation
uses sign-magnitude representation or not. It would thus be useful for
such a test to exist within the limited scope of strictly conforming
programs.
There are many cases where a program may care if the implementation
uses ones-complement or not. It would thus be useful for such a test
to exist within the limited scope of strictly conforming programs.
I suggest that very few modern C programs are likely to care about that.

Representations other than two's-complement are quite rare these days,
and are likely to become more so. If I'm trying to write code that's
painstakingly portable, I'll try to write it so it doesn't *care* how
negative integers are represented. If I need to do bit manipulation,
I'll restrict it to unsigned integers.

If I need to write code whose behavior depends on the representation of
negative integers, I might write something like:

#if INT_MIN == -INT_MAX
#error "Sorry, this code doesn't support this target"
#endif

so the code won't compile *either* on a one's-complement or
sign-and-magnitude implementation *or* on a two's-complement
implementation that treats the most negative value as a trap
representation. I have never used a system that would trigger the
#error.

In principle, sure, it would be nice to have a reliable way to determine
at compile time which representation is used. In practice, I'm
skeptical that it would actually be useful.

My guess (and it's nothing more than that) is that a future standard is
more likely to mandate two's-complement than to provide a mechanism to
detect it.
Post by Jakob Bohm
Finally, the possible absence of both the above useful language
abilities might or might not raise a logical question as to the absence
of information on the handling of negative zero in the standard.
--
Keith Thompson (The_Other_Keith) kst-***@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"
Richard Damon
2016-10-23 21:26:14 UTC
Permalink
Raw Message
Post by Jakob Bohm
Not quite, it replaces 3 interesting questions with one much less
interesting one.
There are many cases where a program may care if the implementation
uses sign-magnitude representation or not. It would thus be useful for
such a test to exist within the limited scope of strictly conforming
programs.
There are many cases where a program may care if the implementation
uses ones-complement or not. It would thus be useful for such a test
to exist within the limited scope of strictly conforming programs.
Finally, the possible absence of both the above useful language
abilities might or might not raise a logical question as to the absence
of information on the handling of negative zero in the standard.
Enjoy
Jakob
A standard conforming way to detect this (I think) would be to take a
value of -1 and mask it with something like 0x7F, which by definition
will be done at the bit level. I.E, look at the value of 0x7F & (-1)

Two's complement will give you 0x7F (127)
One's complement will give you 0x7E (126)
Signed magnitude will give you 1
Tim Rentsch
2016-10-24 01:33:33 UTC
Permalink
Raw Message
Post by Richard Damon
Post by Jakob Bohm
Not quite, it replaces 3 interesting questions with one much less
interesting one.
There are many cases where a program may care if the implementation
uses sign-magnitude representation or not. It would thus be useful for
such a test to exist within the limited scope of strictly conforming
programs.
There are many cases where a program may care if the implementation
uses ones-complement or not. It would thus be useful for such a test
to exist within the limited scope of strictly conforming programs.
Finally, the possible absence of both the above useful language
abilities might or might not raise a logical question as to the absence
of information on the handling of negative zero in the standard.
A standard conforming way to detect this (I think) would be to take a
value of -1 and mask it with something like 0x7F, which by definition
will be done at the bit level. I.E, look at the value of 0x7F & (-1)
Two's complement will give you 0x7F (127)
One's complement will give you 0x7E (126)
Signed magnitude will give you 1
Yes, this technique is standard conforming. It's a little bit
different in C90 (where what is tested is the representation of
'long int') and C99/C11 (in which case it is the representation
of 'intmax_t') for tests in preprocessor conditions. A nice
expression to use for a test is (3 & -1), which is variously
1, 2, or 3, for signed magnitude, ones' complement, and two's
complement.

Tests done in the preprocessor can determine the representation
of only one type, as described above, but that can be matched
with other integer types via assertions or static assertions.
(In principle the representation of signed char or short could be
different than that of int, and AFAIK there is no way to test
the representations of those types via a compile-time test.)
Tim Rentsch
2016-10-24 02:08:24 UTC
Permalink
Raw Message
Post by Jakob Bohm
Post by Tim Rentsch
Post by s***@casperkitty.com
Post by Keith Thompson
For an implementation that supports integer negative zero, is
negative zero required to compare equal to 0 (using the "=="
operator)? If so, where is this specified?
For each form of integer representation, there is one bit pattern which the
Standard would allow implementations to handle arithmetic in any fashion
whatsoever.
For two's-complement representations, that value is ~INT_MAX, for sign-
magnitude implementations it's INT_MIN^INT_MAX, and for ones'-complement
implementations, it's ~0.
The Standard defines a means via which a program can test whether it is
running on a two's-complement implementation that promises not to do
anything weird with the aforementioned bit pattern: it can test whether
INT_MIN < -INT_MAX. Is there any means via which the Standard would
allow a program to do likewise on a sign-magnitude or ones'-complement
implementation? If not, would the Standard impose any requirements on
code which performs arithmetic on "negative zero"?
These seem like pointless or rhetorical questions. And still too
"Is there a way a program can tell if the implementation supports
negative zeros?"
This replaces 118 words by 14 words, and has basically the same
content.
Not quite, it replaces 3 interesting questions with one much less
interesting one.
Apparently one of us is misreading the posting, for as I read it
it is only asking about what happens with (what would be)
negative zeros. The posting has two sentences that end with a
question mark, but in my view they ask what is basically the same
question: what happens if a "negative zero" representation is
used in an expression, ie, is negative zero supported? The
answer is there is no way for a program to tell. So I don't know
what you think the three questions are (unless you perhaps
consider the two different representations to be different
questions). The second question is a non-question: if negative
zeros are not supported, any use of them is undefined behavior,
as is made plainly evident in section 6.2.6.2.
Post by Jakob Bohm
There are many cases where a program may care if the implementation
uses sign-magnitude representation or not. It would thus be useful for
such a test to exist within the limited scope of strictly conforming
programs.
There are many cases where a program may care if the implementation
uses ones-complement or not. It would thus be useful for such a test
to exist within the limited scope of strictly conforming programs.
It is easy to test for which representation is used, as
Richard Damon's posting explains.

As I read the previous posting, it is asking only about what
happens with (what would be) negative zeros, not about which
representation is being used.
Post by Jakob Bohm
Finally, the possible absence of both the above useful language
abilities might or might not raise a logical question as to the absence
of information on the handling of negative zero in the standard.
Whether negative zeros are supported is implementation-defined.
There is no guaranteed method for a program to test if they
are. There is no real value in knowing whether negative
zeros are supported, because they can't be used reliably
anyway, ie, reliably be distinguished from a regular zero.
What do you consider the point of asking the question, except
to ask a silly question?
s***@casperkitty.com
2016-10-24 17:24:21 UTC
Permalink
Raw Message
Post by Tim Rentsch
Whether negative zeros are supported is implementation-defined.
There is no guaranteed method for a program to test if they
are. There is no real value in knowing whether negative
zeros are supported, because they can't be used reliably
anyway, ie, reliably be distinguished from a regular zero.
What do you consider the point of asking the question, except
to ask a silly question?
If an implementation supports negative zero, and combining CHAR_BITS with
the size of an integer type would imply that all bit patterns are accounted
for, code could safely fread() into the storage behind an integer type and
be assured it could not contain a trap representation. In the absence of
a way to test for negative zero I know of no way code could receive such
assurance.
Tim Rentsch
2016-10-24 21:37:42 UTC
Permalink
Raw Message
Post by s***@casperkitty.com
Post by Tim Rentsch
Whether negative zeros are supported is implementation-defined.
There is no guaranteed method for a program to test if they
are. There is no real value in knowing whether negative
zeros are supported, because they can't be used reliably
anyway, ie, reliably be distinguished from a regular zero.
What do you consider the point of asking the question, except
to ask a silly question?
If an implementation supports negative zero, and combining CHAR_BITS with
the size of an integer type would imply that all bit patterns are accounted
for, code could safely fread() into the storage behind an integer type and
be assured it could not contain a trap representation. In the absence of
a way to test for negative zero I know of no way code could receive such
assurance.
A contrived example. No sensible programmer would take binary
input and read it using an implementation-specific representation
such as int unless it had been written out by a program compiled
by the same implementation, in which case there is no problem.

Furthermore, there's an easy and obvious way around a potential
trap representation, simply by checking for a negative zero
bit pattern (easy to generate) and substituting a regular
zero bit pattern (all zeros). So even someone foolish enough
to go down such a path would have an easy time pulling his
ass out of the fire, programmatically speaking.

So I guess the point of asking the question was to respond
with an even sillier answer. Job well done!
s***@casperkitty.com
2016-10-24 23:27:24 UTC
Permalink
Raw Message
Post by Tim Rentsch
Post by s***@casperkitty.com
If an implementation supports negative zero, and combining CHAR_BITS with
the size of an integer type would imply that all bit patterns are accounted
for, code could safely fread() into the storage behind an integer type and
be assured it could not contain a trap representation. In the absence of
a way to test for negative zero I know of no way code could receive such
assurance.
A contrived example. No sensible programmer would take binary
input and read it using an implementation-specific representation
such as int unless it had been written out by a program compiled
by the same implementation, in which case there is no problem.
Or unless the file is *supposed* to have been written out by a program
written out using such an implementation, but the program is required to
behave in constrained fashion even when given a maliciously-crafted input
file (hardly an obscure requirement).
Post by Tim Rentsch
Furthermore, there's an easy and obvious way around a potential
trap representation, simply by checking for a negative zero
bit pattern (easy to generate) and substituting a regular
zero bit pattern (all zeros). So even someone foolish enough
to go down such a path would have an easy time pulling his
ass out of the fire, programmatically speaking.
If an application would meet requirements without needing such a check in
the machine code, and if including such a check in the source code would
make it hard for a conforming compiler not to include it in the machine
code, it would be better to simply have code assert that it is supposed to
be run on a machines that would behave in constrained fashion even without
such a check.
Tim Rentsch
2016-10-25 22:27:09 UTC
Permalink
Raw Message
Post by s***@casperkitty.com
Post by Tim Rentsch
Post by s***@casperkitty.com
If an implementation supports negative zero, and combining CHAR_BITS with
the size of an integer type would imply that all bit patterns are accounted
for, code could safely fread() into the storage behind an integer type and
be assured it could not contain a trap representation. In the absence of
a way to test for negative zero I know of no way code could receive such
assurance.
A contrived example. No sensible programmer would take binary
input and read it using an implementation-specific representation
such as int unless it had been written out by a program compiled
by the same implementation, in which case there is no problem.
Or unless the file is *supposed* to have been written out by a program
written out using such an implementation, but the program is required to
behave in constrained fashion even when given a maliciously-crafted input
file (hardly an obscure requirement).
Post by Tim Rentsch
Furthermore, there's an easy and obvious way around a potential
trap representation, simply by checking for a negative zero
bit pattern (easy to generate) and substituting a regular
zero bit pattern (all zeros). So even someone foolish enough
to go down such a path would have an easy time pulling his
ass out of the fire, programmatically speaking.
If an application would meet requirements without needing such a check in
the machine code, and if including such a check in the source code would
make it hard for a conforming compiler not to include it in the machine
code, it would be better to simply have code assert that it is supposed to
be run on a machines that would behave in constrained fashion even without
such a check.
As usual you are making mountains higher than the Himalayas out
of molehills too small to be seen with the naked eye.

Tim Rentsch
2016-10-19 21:56:44 UTC
Permalink
Raw Message
Post by Nick Bowler
Post by Keith Thompson
I'm assuming that zero and negative zero are the same value, and
more generally that two values that compare equal are the same value.
I don't think this assumption is supported by the standard.
Negative zero and zero are different values with different object
representations. [...]
Clearly negative zero and zero are distinct values. This
follows because there are operations that yield different
(non-zero) results when one operand is a negative zero
rather than a regular zero. An expression like 'x ^ -12',
for example.
Loading...