What's the deal with C99?

E

Eric Sosman

Paul said:
[... dodging every question ...]
*I* am just an interested person. *I* don't have to *prove* anything
to anyone. I don't have anything at stake. Unlike the ANSI C
committee, I don't need to justify my existence.

Nor do you need to justify your accusations, it seems.
 
K

Keith Thompson

santosh said:
And my installation of the Intel C/C++ compiler does seem to support
long double.

And is long double bigger (both in size and in range) than double?

Perhaps whoever wrote Intel's documentation mistakenly thought that
long double is specific to C99 and/or that making long double the same
size as double is non-conforming.
 
K

Keith Thompson

Paul Hsieh said:
Oh that's obvious. There are so *MANY* features that clearly need to
be in the language. The mass exodus from C (and C++) to go to other
languages by itself speaks volumes about what programmers want.
There's no feature added to C99 that addresses even one thing that has
been causing people leave C behind.

It's not clear to me that this is a real problem. Yes, programmers
leave C for other languages for various reasons, but plenty of
programmers have stuck with C for other reasons. C can't be all
things to all people, and it shouldn't try.

[...]
The one feature that there simply is NO controversy about at all:
Memory management. I don't think programmers could yell any louder
for better support for any (practical) other feature.

There's no controversy about memory management? Really?

Ok, you want improved memory management. Can you present (or point
to) a concrete proposal? (I assume you're not referring to automatic
garbage collection; I think the decision not to add that to the
language was a deliberate one, and it's certainly controversial.)

[snip]
 
L

lawrence.jones

Ioannis Vranos said:
long double is a fully supported built-in type of C90. I suppose you are
doing a mistake about this.

I'm just reporting what Intel says in
<http://www.intel.com/support/performancetools/c/sb/cs-015003.htm>:

The Intel(R) C++ Compilers conforms to the ANSI/ISO standard
ISO/IEC 9899:1999 for C language with one limitation:

* long double (128-bit representations) is not supported.

Perhaps the support is mostly there but not quite complete?

-Larry Jones

Yep, we'd probably be dead by now if it wasn't for Twinkies. -- Calvin
 
C

CBFalconer

Paul said:
.... snip ...

The one feature that there simply is NO controversy about at all:
Memory management. I don't think programmers could yell any louder
for better support for any (practical) other feature. (Obviously
the other one is bounds checked arrays, however, that runs into
practicality problems.) Show me a document anywhere that
demonstrates that the ISO C language committee gave a flying fig
about memory management; that they were working towards some sort
of library extensions or API enhancements to deal with the leaking,
corruption and examination of memory.

You obviously don't realize that the fundamental C technique of
'taking addresses' and 'forming pointers' to things in various
types of storage makes such memory checking totally impossible.
Unless you are willing to give up all efficiency by making all
pointers indirect, and keeping a master list, and modifying that on
every *p++ coding.
The other obvious stuff like removing gets, or strtok -- the committee
continued to prop up these functions with rationales. While I
disagree with the main content of TR 24731, one of its features, which
is to provide re-entrant substitutes for many of the standard library
functions has been an issue ever since computers have supported multi-
threading. I.e., this issue is not recent -- and you can't call
Microsoft exactly a "fast mover" by using its influence to deal with
this issue *NOW*.

gets has been declared obsolete for the next standard. There is no
reason to treat strtok in the same manner. strtok can be used
safely. There is no reason that vendors should not supply
re-entrant versions of most of the standard library content.
 
K

Keith Thompson

Ioannis Vranos said:
(e-mail address removed) wrote: [...]
I'm just reporting what Intel says in
<http://www.intel.com/support/performancetools/c/sb/cs-015003.htm>:

The Intel(R) C++ Compilers conforms to the ANSI/ISO standard
ISO/IEC 9899:1999 for C language with one limitation:

* long double (128-bit representations) is not supported.

Perhaps the support is mostly there but not quite complete?

Perhaps C99 "upgraded" long double to larger value range than C90 had it?

No, it didn't.
 
P

Paul Hsieh

It's not clear to me that this is a real problem. Yes, programmers
leave C for other languages for various reasons, but plenty of
programmers have stuck with C for other reasons. C can't be all
things to all people, and it shouldn't try.

You don't get it do you? Its not only that people are looking for
some particular features of other languages. Its that they hate the
*problems* with C. Furthermore, unless you ask, or do some analysis,
you won't know whether or not the features they are leaving C for, are
not trivial to implement (or to counter with an alternate feature).

I, of course, have already my own sample of this sort of feedback just
for Bstrlib. What I find is that people for some reason or another
are trying to create a project in C after a long time away from the
language. The common refrain I get from them is "I was SOOOO dreading
the fact that I would have to go back to using C's strings, but am so
glad you made your library available". Of course its a self-selecting
group of people who use my library, but the number of such people is
not small. So its pretty obvious where the C language has weaknesses,
and in many of those case, there's no justification for shoving people
away from the whole language over something that can easily be worked
around.
[...]
The one feature that there simply is NO controversy about at all:
Memory management. I don't think programmers could yell any louder
for better support for any (practical) other feature.

There's no controversy about memory management? Really?

There's no controversy that its *IMPORTANT*. The C method is a loser
-- my claim is that this is primarily so because its just too minimal;
it does not even make the most minimal effort to address its own
problems. Most other people have decided vote with their feet and
just go to different languages instead.
Ok, you want improved memory management.

What has this got to do with me? Millions of programmers wanted
improved memory management. It seems that every single language
designer in the last few decades wants improved memory management. To
suggest that just *I* want improved memory management is just
preposterous. I can *DO* my own memory management thank you very
much. I've written my own heap, and pool libraries -- *I* don't need
any help from the C language committee. This isn't about *ME*.
[...] Can you present (or point to) a concrete proposal?

I am not a proposal generating beaurocrat. I am not an active
participant in the standards process, and I am not a stakeholder. I
have nevertheless proposed various API modifications in this very
newsgroup over the years just on memory alone. They have never even
been acknowledged by anyone who has influence with the standards
committee.

The (admittedly raw) proposals have typically been in the form of

size_t memTotalAllocated (void);
void * memExpand (...);
size_t memSize(void *);
enum MemType memClassify (void *);
int memCheck (void *);
int memWalk (int (* cb) (void * ctx, int state, void * loc), void *
ctx);

etc. Most of these already exist in other compilers, or in equivalent
forms in various tools. But clearly anyone with the least bit of
interest in keeping the C standard relevant could either take these
suggestions or come up with something on their own.
[...] (I assume you're not referring to automatic
garbage collection; I think the decision not to add that to the
language was a deliberate one, and it's certainly controversial.)

Clearly adding GC to the language would be a huge step, and not really
in the basic *spirit* of C or what the standards committee's modus
operandi is claimed to be (which they claim is to codify existing
practice.) Adding GC, I think, would be contingent on proving that
the technology was reasonable to implement over a wide variety of
compilers, and that the committee was willing to change its role to
that of wanting to add major features and change the target audience
for the C programming language. That is not a bad thing, but it would
be like a kindergartener deciding to write a PhD thesis; its not clear
that they are up to the task.

But this is only one possible way of addressing the problem and you
just don't *need* to go to these extremes.
 
I

Ioannis Vranos

Keith said:
Ioannis Vranos said:
(e-mail address removed) wrote: [...]
I'm just reporting what Intel says in
<http://www.intel.com/support/performancetools/c/sb/cs-015003.htm>:

The Intel(R) C++ Compilers conforms to the ANSI/ISO standard
ISO/IEC 9899:1999 for C language with one limitation:

* long double (128-bit representations) is not supported.

Perhaps the support is mostly there but not quite complete?
Perhaps C99 "upgraded" long double to larger value range than C90 had it?

No, it didn't.


Then I think their long double comment doesn't make any sense.
 
S

santosh

Ioannis said:
Keith said:
Ioannis Vranos said:
(e-mail address removed) wrote: [...]
I'm just reporting what Intel says in
<http://www.intel.com/support/performancetools/c/sb/cs-015003.htm>:

The Intel(R) C++ Compilers conforms to the ANSI/ISO standard
ISO/IEC 9899:1999 for C language with one limitation:

* long double (128-bit representations) is not supported.

Perhaps the support is mostly there but not quite complete?
Perhaps C99 "upgraded" long double to larger value range than C90
had it?

No, it didn't.


Then I think their long double comment doesn't make any sense.

Obviously they are saying that 128 bit long doubles are not yet
supported. That doesn't mean that a smaller long double isn't.
Currently they seem to be using 96 bit long doubles.
 
K

Keith Thompson

santosh said:
Ioannis said:
Keith said:
(e-mail address removed) wrote:
[...]
I'm just reporting what Intel says in
<http://www.intel.com/support/performancetools/c/sb/cs-015003.htm>:

The Intel(R) C++ Compilers conforms to the ANSI/ISO standard
ISO/IEC 9899:1999 for C language with one limitation:

* long double (128-bit representations) is not supported.

Perhaps the support is mostly there but not quite complete?
Perhaps C99 "upgraded" long double to larger value range than C90
had it?

No, it didn't.


Then I think their long double comment doesn't make any sense.

Obviously they are saying that 128 bit long doubles are not yet
supported. That doesn't mean that a smaller long double isn't.
Currently they seem to be using 96 bit long doubles.

They're saying that they don't support 128-bit long double, which is
correct.

They're also saying that this is a limitation on their conformance to
the C99 standard, which is incorrect, since C99 doesn't require
128-bit long double.

I've just submitted a comment on this to Intel via the web page's
feedback link.
 
L

lawrence.jones

santosh said:
Obviously they are saying that 128 bit long doubles are not yet
supported. That doesn't mean that a smaller long double isn't.
Currently they seem to be using 96 bit long doubles.

Their long double only has 96 value bits but its size is 16 (8 bit)
bytes, so it's 128 bits in size. And there's no requirement in C99 to
support 128 bit long doubles, so even if they did want to support long
double with 128 value bits sometime in the future, it wouldn't be a
conformance issue now. I can only assume that their long double support
is deficient in some way, but their conformance statement certainly
doesn't provide any hints as to exactly what the deficiencies are.

-Larry Jones

I thought my life would seem more interesting with a musical
score and a laugh track. -- Calvin
 
P

Paul Hsieh

Paul Hsieh wrote:
... snip ...

You obviously don't realize that the fundamental C technique of
'taking addresses' and 'forming pointers' to things in various
types of storage makes such memory checking totally impossible.

What the hell is your problem? You know very well that I am a supreme
expert about such things.
Unless you are willing to give up all efficiency by making all
pointers indirect, and keeping a master list, and modifying that on
every *p++ coding.

When you are in a corrupted state, indeed there is nothing you can do
that is *guaranteed*. But there is a lot you can do with 99.99%
certainty. This is the whole premise behind the very concept of a
debugger, for example.

My point is that it is very typical that debuggers which are too
general, have very little insight into the state and structure of
memory. Yet, with some work, its easily possible to make a very
useful self-checking memory manager that takes very little performance
and very little extra space overhead. Having written a heap manager
yourself, you must be very well aware of this -- it costs basically
nothing to have a very rich debugging environment for memory.

For example, its trivial to track the base/system heap memory regions
that your manager allocates from. So writing a function like:

int isInAllocatedMemoryRange (void * ptr, int size);

is both useful and easy to do, and *you* of all people ought to know
that you can do this. Of course its not a guarantee that the pointer
is in the right format, or aligned properly, or pointing at live
allocated memory. So we can try to add the following:

int isValidAllocatedMemoryBase (void * ptr);

which would first make sure the allocation was in range, then check
the pointer format, then check the headers for a valid allocation
pattern. Of course this isn't guaranteed to be correct, because you
might be pointing to the middle of an allocation which happens to have
a coincident pattern. But saying this is ineffective, is like saying
the SHA2 secure hash function doesn't work because there might be
collisions. There is a *theoretical* sense in which that's true, but
not a practical one. So we continue:

size_t sizeOfMemoryAllocation (void * ptr);

While one might imagine a heap design where the above might not be
obtainable, this is marginal, and a default of 0 for systems that
can't compute this is not fatal, and could be written into the
specification.

Ok so what next? Well, the headers have to be there for live
allocations, but the contents of free-entries doesn't really matter,
and so is overloaded with data that is convenient for doing free-entry
look-up right? (That's how I did it anyways -- I just do a linked
list, but in general it could encode a B+ tree, to guarantee O(ln(n))
worst case look-up time while always returning allocations when it is
possible.) That means its fairly straight forward to support a
complete heap walking mechanism with no additional overhead for
allocated memory:

int memAllocationsIterate (void (* cb) (void * ctx, void * ptr),
void * ctx);

Handing pointers to free entries to the callback probably doesn't make
any sense outside of system specific purposes, so its just as well to
skip them. Also for multithreading reasons, it doesn't make sense to
make a per-allocation iterator, but instead demanding that the whole
for-loop be encoded in the library under one lock is the soundest way
to go. A heap design which cannot support this could just return with
some value indicating that this is not supported. We can see also,
that this is an alternate way of implementing the function
isValidAllocatedMemoryBase () (nobody says this stuff has to be fast;
remember what we are trying to do and what we are competing against).

This function has a very serious point to it, of course. If you've
corrupted your heap, this iterator is almost certainly not going to
complete and has a very high likelihood of detecting the corruption
(even if the corruption happens in the free-list) and can typically do
so long before your program crashes if used strategically. So using
binary search, you can typically narrow down when any deterministic
program has corrupted its own heap with *near* certainty.

Now, the C method of malloc/free, has generally higher amortized
performance versus garbage collection. But this is typically a non-
issue, since the true memory related speed usually has to do with the
speed at which these allocations are *filled in*, not how fast they
are managed. While alignment is the main way in which C tries to deal
with this, there is no advantage over GC here. Where the *real*
advantage is, is in the function *realloc()*. The problem is that
when it moves the memory and when it just expands the allocation
cannot be determined beforehand by the application. In the case of
resizable arrays, this is particularly infuriating, since the resize
length is typically heuristic but the real cost of doing resizing can
easily be limited by the realloc move performance. So let's suppose
we had:

size_t memExpand (void * ptr, size_t minSize, size_t maxSize);

which will only perform a realloc if the pointer is a valid heap
pointer and stays in the same place, and it can be resized to a length
of *at least* minSize, and maxSize >= minSize. Otherwise 0 is
returned and nothing happens. The function will try to reallocate the
size to as large as possible up to a length of no more than maxSize.
So for an array resize, let us say that we want to write to arr[n+1],
but we've only allocated space for n items. We want to resize it to
2*n to decrease the overhead of future resize calls, however we don't
want to incurr the cost of a memory move if we don't have to. So we
do this:

if (n*sizeof (*arr) >= mlen) {
if (0 == (mlenp = memExpand (arr, (n+1)*sizeof (*arr), mlen +
mlen))) {
void * arrp = realloc (arr, mlen + mlen); /* Will likely
memmove */
if (NULL == arrp) return OUT_OF_MEMORY;
arr = arrp;
mlen += mlen;
} else {
mlen = mlenp;
}
}
writeTo (&arr[n+1]);

So the point of all this is that the high cost of reallocs still are
limited to at most lg2(sizeof(size_t)) actual memcpy()s while
postponing the last large allocation as much as possible and in many
cases avoiding it rather than incurring the cost of one extra memcpy
at the peak size (when the cost is highest). And obviously if a
system can't deal with this, just return 0 all the time. The GC
people don't even play in this arena, but they would ordinarily
counter by saying that realloc has unpredictable performance (since it
might memcpy() with arbitrarily high probability). The above shows a
way of maximally negotiating this performance cost and in real world
code the performance advantage over GC gets widened.

Then finally, its totally trivial to implement the following:

size_t memTotalAllocations (void);

Which would just keep track of either the total memory allocation
requested or the amount committed. Because of alignment adjustment
and the semantics of realloc, it seems that tracking the amount of
*committed* memory is easier. I.e.:

int main () {
void * p = malloc (SIZE_1);
void * r = realloc (p, SIZE_2);
free (r ? r : p);
return memTotalAllocation ();
}

(ignoring the ANSI issues for the moment; its just an example) would
always return 0. So its not hard to see how using this will help
developers track down memory leaks.

In any event, one can see that if such a set of functions were added
to the standard it would radically improve the language's ability,
power, real world safety and usability drammatically. The people who
endorse GC languages would have absolutely no response to these
extensions. And the capabilities would go a long way to cutting the
legs out from underneath the argument that C's memory management is
too unusuable.

One can also see how the whole "why don't you write up a proposal"
nonsense is such a strawman. This stuff practically just rolls of the
tongue. Everyone has seen at least some of these in the extensions of
their compilers, and this is not the first time I or others have
mentioned any of these ideas here.
gets has been declared obsolete for the next standard.

Well, deprecated anyways. It seems that excoriating them for their
nonsensical rationale was at least somewhat effective. Of course none
of this matters if nobody adopts the standard.
[...] There is no
reason to treat strtok in the same manner. strtok can be used
safely. There is no reason that vendors should not supply
re-entrant versions of most of the standard library content.

So I assume that you oppose TR 24731 then? (I do for other reasons,
but I think all their re-entrant function replacements should be
accepted.) If so, then you do not speak for or with the committee as
they seem to be in the process of approving it, in which case these
comments are not really directed at you.
 
C

CBFalconer

Paul said:
What the hell is your problem? You know very well that I am a supreme
expert about such things.


When you are in a corrupted state, indeed there is nothing you can do
that is *guaranteed*. But there is a lot you can do with 99.99%
certainty. This is the whole premise behind the very concept of a
debugger, for example.

My point is that it is very typical that debuggers which are too
general, have very little insight into the state and structure of
memory. Yet, with some work, its easily possible to make a very
useful self-checking memory manager that takes very little performance
and very little extra space overhead. Having written a heap manager
yourself, you must be very well aware of this -- it costs basically
nothing to have a very rich debugging environment for memory.

For example, its trivial to track the base/system heap memory regions
that your manager allocates from. So writing a function like:

int isInAllocatedMemoryRange (void * ptr, int size);

is both useful and easy to do, and *you* of all people ought to know
that you can do this. Of course its not a guarantee that the pointer
is in the right format, or aligned properly, or pointing at live
allocated memory. So we can try to add the following:

I have ignored the remainder. Getting too complex for me.
However, my point is that there is no way (apart from comprehensive
runtime tables) to tell that a pointer is to malloced memory,
static memory, or automatic memory. There is also no way to tell
that that pointer is the the whole chunk, or to a miniscule chunk
of that. This makes memory checking virtually impossible, if you
want to preserve efficiency.

Sure, I can do some things with a subset of those pointers, which I
have allocated and manipulated since birth. But those operations
won't function on another system. They will be totally confused if
ever passed non-malloced pointers, or even malloced pointers using
another malloc package.

I just don't like any software that 'works sometimes'. If its
purpose is to tell me that memory is being misused, I want a
definite answer. Barring that, I prefer no answer.
 
D

Dann Corbit

Their long double only has 96 value bits but its size is 16 (8 bit)
bytes, so it's 128 bits in size. And there's no requirement in C99 to
support 128 bit long doubles, so even if they did want to support long
double with 128 value bits sometime in the future, it wouldn't be a
conformance issue now. I can only assume that their long double support
is deficient in some way, but their conformance statement certainly
doesn't provide any hints as to exactly what the deficiencies are.

I suspect that the real reasoning is that they do not want to support
another floating point type because:
1. Long double is a lot more difficult to get right (so that the answers
returned are close to 1/2 ULP in accuracy).
2. Math library has 2/3 as much code (with only float and double but not
long double implementation) so a lot less effort supporting it, testing it,
documenting it, etc.
3. If 128 bit platforms are also included, it would mean yet another
version. Of course, the Cephes collection handles all of this nicely. I
think it would be good to make the Cepehs collection a "Reference"
implementation which can be selected or ignored because it is so fully
complete.

Anyway, I think that the reasons are pure laziness {especially considering
that they already had the routines written long ago since they were included
with the 16 bit compiler}. The worst thing is that Intel followed suit
after MS dropped the ball.

In C++ there is a nice work around, since I can use QD:
http://crd.lbl.gov/~dhbailey/mpdist/
to create my own double-double and quad-double libraries to get lots more
precision if I want it but using C it would be a real pain. Without a
hardware type, intermediate precisions like that become an ugly mess of
library calls instead of the simple, fundamental algorithms that they ought
to be.
 
S

santosh

Their long double only has 96 value bits but its size is 16 (8 bit)
bytes, so it's 128 bits in size.

Hmm. On my installation here sizeof(long double) gives me 12.
And there's no requirement in C99 to
support 128 bit long doubles, so even if they did want to support long
double with 128 value bits sometime in the future, it wouldn't be a
conformance issue now. I can only assume that their long double
support is deficient in some way, but their conformance statement
certainly doesn't provide any hints as to exactly what the
deficiencies are.

Yes. And till now all my programs with long doubles have worked just
fine, as far as I can see, with their compiler, but I haven't used it
extensively.
 
K

Keith Thompson

CBFalconer said:
Paul Hsieh wrote: [...]
For example, its trivial to track the base/system heap memory regions
that your manager allocates from. So writing a function like:

int isInAllocatedMemoryRange (void * ptr, int size);

is both useful and easy to do, and *you* of all people ought to know
that you can do this. Of course its not a guarantee that the pointer
is in the right format, or aligned properly, or pointing at live
allocated memory. So we can try to add the following:

I have ignored the remainder. Getting too complex for me.
However, my point is that there is no way (apart from comprehensive
runtime tables) to tell that a pointer is to malloced memory,
static memory, or automatic memory. There is also no way to tell
that that pointer is the the whole chunk, or to a miniscule chunk
of that. This makes memory checking virtually impossible, if you
want to preserve efficiency.
[...]

There is no *portable* way to check this, at least not in current
standard C. There may well be a reliable way to do it for some
implementations. Other implementations might, for whatever reason,
make this kind of thing difficult or impossible (this is speculation
on my part).

I have serious doubts about whether the kind of functionality being
proposed could be specified with sufficient precision and rigor that
it could be added to the language standard.

On the other hand, perhaps it would make more sense as (part of) a
secondary standard that wouldn't necessarily be implementable on all
systems that can support standard C.
 
J

jacob navia

Dann said:
3. If 128 bit platforms are also included, it would mean yet another
version. Of course, the Cephes collection handles all of this nicely. I
think it would be good to make the Cepehs collection a "Reference"
implementation which can be selected or ignored because it is so fully
complete.

Cephes is very good. Lcc-win uses Cephes internally. I have rewritten
the 352 bit part of it in assembler, extended some functions, and
integrated them into the package.

[snip]
In C++ there is a nice work around, since I can use QD:
http://crd.lbl.gov/~dhbailey/mpdist/
to create my own double-double and quad-double libraries to get lots more
precision if I want it but using C it would be a real pain. Without a
hardware type, intermediate precisions like that become an ugly mess of
library calls instead of the simple, fundamental algorithms that they ought
to be.

Not in lcc-win since lcc-win accepts operator overloading,
precisely because of the points you make above.
 
D

Dann Corbit

jacob navia said:
Cephes is very good. Lcc-win uses Cephes internally. I have rewritten
the 352 bit part of it in assembler, extended some functions, and
integrated them into the package.

Did you catch the original register errors in the provided assembly?
E.g. there was an assignment to AX when EAX was clearly intended. It has
the very bad habit of working sometimes.
[snip]
In C++ there is a nice work around, since I can use QD:
http://crd.lbl.gov/~dhbailey/mpdist/
to create my own double-double and quad-double libraries to get lots more
precision if I want it but using C it would be a real pain. Without a
hardware type, intermediate precisions like that become an ugly mess of
library calls instead of the simple, fundamental algorithms that they
ought to be.

Not in lcc-win since lcc-win accepts operator overloading,
precisely because of the points you make above.

Quite frankly, I like the qfloat implementation in your package.
 
J

jacob navia

Dann said:
Did you catch the original register errors in the provided assembly?
E.g. there was an assignment to AX when EAX was clearly intended. It has
the very bad habit of working sometimes.

I have rewritten the package completely in assembly.
It is around 30-40% faster. In the 64 bit version I
have extended the qfloat numbers from 14 bytes to 16 bytes,
increasing precision. I will then change the basic
algorithms to increase precision without increasing
the size of the number.
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top