4 ms·
My understanding is that a lot of undefinedness in the original c89 standard came from a desire to support non 2 complement machines like Burroughs. Of course i
by nn3 8y ago
My understanding is that a lot of undefinedness in the original c89 standard came from a desire to support non 2 complement machines like Burroughs. Of course in hindsight that was a total mistake. Yes would be a great idea to come up with an modern update to the c standard that is specified on let's say the level of java.
- rootbear 8y agoAs someone who was on X3J11 at the time C89 was ratified, I can tell you that supporting 1s-compliment architectures was necessary. The standard would not have been approved if 2s-compliment had been mandated. Standards are all about compromise, even when you know that that will cause trouble later.
- ropeadopepope 8y ago> I can tell you that supporting 1s-compliment architectures was necessary Can you elaborate on that point? Why was it necessary then, but not still necessary now?
- rootbear 8y agoSorry, I didn't mean to imply that there are no 1s-compliment systems still around. The comment I was replying to suggested it was a mistake to support 1s-compliment architectures because that was the source of some of the Undefined Behavior (UB) that so troubles C. My point was that not supporting 1s-compliment wasn't an option then (and probably is not one now.) After all, without 1s-compliment support, how could I write new C apps for the Apollo Guidance Computer? I would like to give an example of how supporting both 1s- and 2s-compliment is the source of a specific UB, but I can't take the time to do that right now, regrettably. Similarly, supporting both Big Endian and Little Endian was necessary. As was supporting ASCII, EBCDIC, and probably Fieldata and five level Baudot (looking at you, trigraphs). All of this generality made it hard to say anything useful in some areas, so you end up in some cases just calling it Undefined, since there was no consensus on how it should be defined.
- jonny_eh 8y agoI suppose you both can be right. It could have been required to support 1s-compliment systems at the time, but also an unfortunate mistake in retrospect.
- insulanus 8y agoWhen issues like this come up, I think the right outcome is to support the popular choice as the default, and support the unpopular choice, but with a penalty. For example, forcing extra code to be inserted, that is, a check (or even a call out to a library), so one unified semantic is followed, and there is no undefined behaviour. Of course, you wouldn't mandate how the semantics are followed. The problem is, it was very hard for us to accept this kind of solution when we grew up feeling like every cycle counted, so that's what I blame for a lot of the UB we see today in the standard.
- makomk 8y agoIf I remember correctly, one of the things that's likely changing in the next version of the C standard is that arithmetic will be required to be 2s-compliment. There just aren't enough machines around which use anything else anymore.
- loup-vaillant 8y ago> All of this generality made it hard to say anything useful in some areas, so you end up in some cases just calling it Undefined, since there was no consensus on how it should be defined. This is a very valid justification of implementation defined. Maybe even unspecified. But undefined? How could you justify that? Even if some platform does not support something at all, and goes bananas whenever you do it (say, signed overflow that traps), what would stopped the standard to say that whether the stuff is undefined or not is platform dependent? Perhaps the committee didn't anticipate how unreasonable compiler writers turned out to be?
- rootbear 8y agoI agree that you want as few UBs as possible. I found the text below from the Rationale useful for understanding these issues. An important feature of UB is that it doesn't require the compiler to catch certain things that can be very hard to catch, such as dereferencing NULL pointers. As noted, it also provides opportunities for enhancing the language in ways that don't break conforming programs. An implementation is allowed to treat Undefined Behavior as Implementation Defined, simply be saying what the implementation does in such cases. But it's not required to say what it does. ---- The terms unspecified behavior, undefined behavior, and implementation-defined behavior are used to categorize the result of writing programs whose properties the Standard does not, or cannot, completely describe. The goal of adopting this categorization is to allow a certain variety among implementations which permits quality of implementation to be an active force in the marketplace as well as to allow certain popular extensions, without removing the cachet of conformance to the Standard. Appendix F to the Standard catalogs those behaviors which fall into one of these three categories. Unspecified behavior gives the implementor some latitude in translating programs. This latitude does not extend as far as failing to translate the program. Undefined behavior gives the implementor license not to catch certain program errors that are difficult to diagnose. It also identifies areas of possible conforming language extension: the implementor may augment the language by providing a definition of the officially undefined behavior. Implementation-defined behavior gives an implementor the freedom to choose the appropriate approach, but requires that this choice be explained to the user. Behaviors designated as implementation-defined are generally those in which a user could make meaningful coding decisions based on the implementation definition. Implementors should bear in mind this criterion when deciding how extensive an implementation definition ought to be. As with unspecified behavior, simply failing to translate the source containing the implementation-defined behavior is not an adequate response.
- Someone 8y agoThere’s also the fact that registers may be larger than the numbers stored in them. The best known example is that of the 80-bit floats of the 8087 FPU, but that’s relatively rare compared to loading integers into larger registers. For example, if you compile if( foo + 10 > bar) goto baz to assembly similar to move foo to R1 move bar to R2 add 10 to R1 subtract R2 from R1 branch-if-greater-than-zero baz and foo and bar are 32-bits, that add can overflow if R1 and R2 also are 32-bits, but never overflows if R1 and R2 are 64-bit. That changes the result of the comparison if, for example foo = INT_MAX bar = INT_MAX The designers of the CPU with 64-bits registers may not want to add variants of add (and subtract, mult, etc) that work on 32 bits (and 16 bits, and 8 bits). They also won’t want to have their C compiler slowing down this kind of code, only because other (often relatively old and slow) CPUs exist.
- deleted 8y ago[deleted]
- Asooka 8y agoBut that's trivially solvable (on x86 at least). You do your arithmetic in rax and then compare the result in eax. You've got the "mask the high 32 bits" operation for free. At worst you'll need to do one bitwise and before using the value for cases where the high bits being poisoned matters. Or let me add annotations to arithmetic expressions that "this expression will definitely not need expensive cleaning up afterwards, promise", which I will need to use in maybe a dozen super-hot loops in the whole codebase. FWIW, we compile all our code with -fwrapv and don't notice any slowdown.
- Someone 8y ago”But that's trivially solvable (on x86 at least)” Even if that’s true (I don’t know enough of x86 assembly to judge that, but one thing I can think of that it may require extra register-to-register moves), that it isn’t an issue for one architecture won’t help a committee discussing the C standard. Also, others in this thread describe what you call “let me annotate” as “forking C”.
- BeeOnRope 8y ago