5 ms·
I think it's funny. C was originally invented in an era when machines didn't have a standard integer size, 36-bit architectures were at their heydays, so C inte
by segfaultbuserr 7y ago
I think it's funny. C was originally invented in an era when machines didn't have a standard integer size, 36-bit architectures were at their heydays, so C integers - char, short, int, and long - only have a guaranteed minimum size that could be taken for granted, but nothing else, to achieve portability. But after the computers of world have converged to multiple-of-8-bit integers, the inability to specify a particular size of an integer become an issue. As a result, in modern C programming, the standard is to use uint8_t, uint16_t, uint32_t, etc., defined in <stdint.h>, C's inherent support of different integer sizes are basically abandoned - no one needs it anymore, and it only creates confusions in practice, especially in the bitmasking and bitshifting world of low-level programming. Now, if N-bit integers are introduced to C, it's kind of a negation-of-the-negation, and we complete a full cycle - the ability to work on non-multiple-of-8-bit integers will come back (although the original integer-size independence and portability will not come back).
- GTP 7y agoWhy portability will not come back? Also when the ability of working on non-multiple-of-8-bit integers was lost?
- gumby 7y agoAFAIK the last machine in widespread production that handled multi-length integers was the PDP-10/20 and its clones which essentially died around 1984. I say "around" because though DEC canceled the 20 line, some clones remained (that was Cisco's original business plan, for example)
- ajuc 7y agoThere was a series of Polish mineframes called Odra with 24bit integers which also died out in 80s essentially, but some of them were still used till 2010 in some specialized railway station software, and there was a short series of faster replacement processors for them made in late 80s-early 90s called SKOK. Of course they weren't "widespread" for most meanings of that word :)
- burfog 7y agoYou can get that experience today with the C compiler for the EZ80 chip. The short is 16-bit, and the long is 32-bit, so logically the int is 24-bit. You can use it as int24_t if you prefer. An EZ80 is used in some of the TI graphing calculators.
- gumby 7y agoOn the PDP-10/20 bytes could range from 1 to 63 bits wide.
- segfaultbuserr 7y agoWell, it's not lost in C itself. But in the practice of modern C programming, it's often sacrificed in favor of using integers of exact sizes (uintN_t), and many programs perform bitwise operation by assuming an exact size of integer. By C99, they are guaranteed to have the same number N of bits across all implementations, and they are included only if the implementation supports it. So the programs using them is standard C, but not 100% portable, there is no requirement in C to implement exact-width integers. Although modification of most programs shouldn't be difficult (there is uint_leastN_t), also, C compilers can be modified to treat extra bits as if they don't exist to allow existing programs to work again.
- pjc50 7y agoThis is the other way round though; code using "int" is a disaster for portability, because you don't know what you're actually getting. So people use uintN_t to get something specific which behaves the same way on different platforms - i.e. portable. You can always #define or typedef uintN_t to a machine type. You can't re-typedef int. You can #define it, but people will hate you.
- burfog 7y agoI'm surprised that it isn't normal to provide a pragma for that. Something like this: #pragma push intsizes 1 2 2 4 0 #include <yimage.h> // old 16-bit header #pragma pop #pragma push intsizes 1 2 4 8 16 #include <servertypes.h> // uses 128-bit long long #pragma pop
- pjc50 7y agoThat makes it completely impossible while reading code to tell what the size of int is even on your system. And worsens header file include order problems.
- TwoBit 7y agoEven worse than int is long. So much old Unix code that was (incorrectly) using long to store pointer-sized types got broken on other 64 bit platforms.
- BenoitEssiambre 7y agoYes this is kind of funny. My understanding is that Thomson and Richie deliberately left out non power-of-two word size support (which other languages had at the time because cpu manufacturers were adding a couple of bits at a time to new models (Get this years 14bit cpu, two more than last years' cpu!). To make a more simple, more elegant, more portable language they decided to settle on power-of-two word lengths. This is similar to how Unix came about, leaving out the cruft and complexity from the over engineered Multics.
- anticensor 7y agoThe requirement is not to be power of two, it is to be multiple of sizeof(char).
- BenoitEssiambre 7y agoI guess I just never encountered the 3,5,6,7 bytes word types.
- vardump 7y ago24-bit floats "FP24" at least have been a thing in some ATI graphics cards (R300 & R420) 15+ years ago. Some DSPs have also used (some still do?) 24-bit word width. These odd word widths are anything but common, though.
- ajtulloch 7y agoModern AMD GPUs still support 24 bit integers, see https://rocm.github.io/GCN_Float16.html https://rocm.github.io/GCN_Float16.html.
- phoe-krk 7y agoCommon Lisp programmer here. While contemporary implementations are most commonly tailored to use (UNSIGNED-BYTE 8), (UNSIGNED-BYTE 16), (UNSIGNED-BYTE 32), and (UNSIGNED-BYTE 64) along with their signed counterparts, our language allows one to freely specify and use integer types such as (UNSIGNED-BYTE 53) that could - in theory - be optimized for on architectures that use unique, by today's standards, word sizes. This also comes from the fact that Common Lisp was specified during times that had no real standardized word sizes, and so the standard had to accomodate for different machine types on which a byte could mean different and mutually exclusive things.
- m463 7y agoWhy not just go to values? Some languages like Ada allow a type that say goes from -273 to 600.
- Gibbon1 7y agoI would very much prefer this and the ability to spec what happens on overflow. There are a lot of arguments back and forth because there actually is no 'right way' to handle overflow.
- guerby 7y agoAnd GNAT (GCC Ada front-end) will use a biased representation for range type when packing tight: with Ada.Text_IO; use Ada.Text_IO; procedure T is type T1 is range 16..19; type T2 is range -7..0; type R is record A : T1; B,C : T2; end record; for R use record A at 0 range 0 .. 1; B at 0 range 2 .. 4; C at 0 range 5 .. 7; end record; X : R := (17,-2,-3); begin Put_Line(X'Size'Image); -- 8 bits end T;
- weinzierl 7y agoPascal has it too. I always found it quite natural to specify the range I want with the data type and not as precondition in the function. Another big advantage is that you can avoid a good deal of potential off-by-one errors, if you define your data types appropriately. For example the following definition in Pascal would be much less error prone than the corresponding definition in C[1]: var weekday: 0 ... 6; monthday: 1 ... 31; For a quarter of a century I wonder why no one seems to miss that feature. I really hope we will get them in C one day. Even more so I hope that the proposals for refinement types in Rust[2] will one day be resolved and become implemented. [1] https://www.cplusplus.com/reference/ctime/tm/ https://www.cplusplus.com/reference/ctime/tm/ [2] https://github.com/rust-lang/rfcs/issues/671 https://github.com/rust-lang/rfcs/issues/671
- AaronFriel 7y agoThere's something fundamentally different and mistaken about C's original implementation of variable integer sizes though. People often describe C as "portable assembly", but despite this, integer sizes varying on different platforms results in non-portability of anything those programs _produce_. That is, a "file", or bit stream (not byte stream!) produced by one machine may be incompatible with another. The original integer-size independence is decidedly not portable. That was probably less of a problem when it was rare to send data from one physical machine to another machine, let alone one of another type. But now the world is inter-net-worked and we have all sorts of machines talking to each other all the time. Making the interfaces explicit reduces errors. These days we now even have virtual machines and programs running at different bit widths on the same machine, and emulated machines on the same machine running different ISAs! I'm also part of what I'm sure is a small number of users who believe using "usize" should be a lint error manually overridden on Rust and also thinks endianness should also be explicit. Heck, it should be a compiler error to write a struct to a socket if it contains any non-explicit values!
- TwoBit 7y agoOriginal C was portable to -platforms- and not to -users-.
- AaronFriel 7y agoAnd in an inter-networked model, that means it's not portable.
- begriffs 7y agoAs you mention, the fundamental integer types have guaranteed minimum sizes (or pedantically a range that matches the following sizes if twos' complement is used): * >=8 bits: char (CHAR_BIT is exactly 8 in POSIX) * >=16 bits: short and int * >=32 bits: long * >=64 bits: long long The C99 typedefs like uint16_t have to be chosen internally to be one of the underlying types. For those sizes that have no matching underlying type, the implementation will omit typedefs. However don't forget the more flexible C99 typedefs int_leastN_t and int_fastN_t. They both will give you a type of at least N bits, where the "fast" one chooses whichever type is most convenient for the processor, and the "least" version picks whichever is smallest. (For instance int_least16_t is probably short, and int_fast16_t is probably int.)
- segfaultbuserr 7y agoYes, that's right, thanks for your elaboration.