Natural size: int

  • Thread starter Frederick Gotham
  • Start date
M

Malcolm

--
www.personal.leeds.ac.uk/~bgy1mm
freeware games to download.

Stephen Sprunk said:
You mean an integer which is not required to hold values outside the
range -32767 to 32767, and which is probably the fastest integer type.
My programmers are bunny rabbits. They only count to five. Anything bigger
than that is "haraba".
What the manufacturer wants has nothing to do with what I want or what the
compiler writers want. I want my code to (1) work, and (2) run as fast as
possible. The manufacturer wants to extract money from my wallet. There
are no shortage of cases where 32-bit ints are faster than 64-bit types on
a processor that "kindly provided 64 bit registers."
The nice engineer, believe me, wants you to have a machine that will run
your programs efficiently. Of course the nasty money men who run his company
are trying to screw money out of the nasty money men who run yours, but that
hardly affects people like us.
If I need an integer with at least 64 bits, I'll use long long; if I want
a fast integer, I'll use int; if I want a fast integer with at least 32
bits, I'll use long. They may not be the same size, and it's up to the
compiler folks to pick which combination is best for a given platform.
You see I just want an integer. Integers count things. If it can't count the
bytes in the computer, then I would be annoyed.
And such weird and wonderful things are allowed by the standard, because
they existed prior to its creation and the purpose of ANSI C was, for the
most part, to document what existed and not to create an ideal language.

You can assume int or long (or long long) is good enough, and in most
cases you'll be right, but that non-portable assumption will eventually
come crashing down -- typically while you're doing a demo for a customer,
or years after the original coder left and you're stuck with maintaining
his crap. Use size_t and you'll never need to worry about it. Thanks,
ANSI.
As long as your assumption that 32 bits is enough is accurate. If you assume
that an integer can count anything, then it is responsibility of the
compiler maker to make sure it can count anything, or at least anything that
is likely to crop up on that machine - you may have a company with over
32000 employees, for example, but it is not going to try to run its payroll
on a machine with 16-bit registers.
 
A

Ancient_Hacker

Skarmander wrote:

If there's no need for the compiler to be able to do what you ask, then how
is your program portable? You're asking for a system that implements what
you want it to implement, or else your program will not run on it.

Well, at least the compiler will print at compile time: "Sorry, I
can't do 35 decimal digits" instead of "TRAP-INTOVFLO" at inconvenient
moments.

Reasonability is in the eye of the beholder, but anyone who would deny C's
usability or portability, even when only restricted to numerical types, is
being deliberately obtuse.

Most of the C programs I see have portability grafted on thru very
clever sets of

#ifdef HP_UX || AIX_1.0
#endif

I may be way iconoclastic, but I don't consider that "portability".

If you're asking in general how languages are created and adopted, and why
the Right Language always seems to lose to the Right Now Language, that's
quite another topic.

Yep, it happens almost every time.

Thanks for your thoughful comments.
 
C

Chris Torek

I think it's high time a language has the ability to do the very basic
and simple things programmers need to write portable software: the
ability to specify, unambiguously, what range of values they need to
represent, preferably in decimal, binary, floating, fixed, and even
arbitrary bignum formats. Not to mention hardware-dependent perhaps
bit widths.

You are not the first to observe this; and it certainly seems like
a reasonable request.
There's no need for the compiler to be able to actually
*do* any arbitrarily difficult arithmetic ...

I think that if one drops this requirement, one winds up with
compilers that promise the moon, but deliver only cheese.
The biness of C having wink-wink recognized defacto binary integer
widths is IMHO just way below contempt.

And yet, oddly, it sort-of mostly works. Kinda, sorta. :)
why can't we get something usable, portable
and reasonable now quite a ways into the 21st century?

The problem is, apparently it is quite difficult. We keep ending
up with PL/I. :)

(Pascal's, or Ada's, integer ranges seem to mostly work OK. Lisp's
handling of integers works even better. The real problems seem to
lie in floating-point. Well, that, and language design is just
plain hard: unless one limits the problem domain quite strictly,
the usual result is something horribly complicated, and yet never
complete.)
 
K

Keith Thompson

Ancient_Hacker said:
Most of the C programs I see have portability grafted on thru very
clever sets of

#ifdef HP_UX || AIX_1.0
#endif

I may be way iconoclastic, but I don't consider that "portability".

(Or something like that; AIX_1.0 isn't a valid preprocessor symbol,
but the point is clear enough.)

Ideally, a large portion of a well-written C application is portable,
and doesn't require such preprocessor tricks. Some things,
particularly involving interaction with the operating system, are
going to require non-portable solutions. The various "#ifdef" tricks
are a way to manage that non-portability -- which should still be kept
as isolated as practical.

It's entirely possible to write useful C code that's entirely portable
to any conforming hosted implementation. It's also entirely possible
to write C code that's 90% portable, with only carefully controlled
portions of it using non-portable constructs.

And, of course, it's also entirely possible to write a non-portable
mess in *any* language. Arguably C makes this easier than some other
languages, but it doesn't make it necessary.
 
S

Skarmander

Ancient_Hacker said:
Skarmander wrote:



Well, at least the compiler will print at compile time: "Sorry, I
can't do 35 decimal digits" instead of "TRAP-INTOVFLO" at inconvenient
moments.
Overflow detection for integers is a sticky issue, especially with C, which
essentially offers no portable way to detect overflow (so the only way to
deal with it is to make sure it never happens, which is a pain). C's excuse
is that not all platforms have overflow detection in the first place, and
for those that do the cost of overflow checking may be unacceptable. Still,
a maybe-not-available-but-standardized-if-it-is language mechanism might
have been preferable.

But aside from that, a compiler couldn't detect overflow at compile time,
except for very obvious cases that even a C compiler or static checking tool
might diagnose. As I understood it, you wanted the compiler to refuse things
like a request for a floating-point type with 4 significant digits in base
12 if it can't reasonably calculate with such a type.

The best a language (and a compiler) could offer you is a single definition
of overflow and a promise to consistently detect it, but avoiding or
handling it would still be up to the programmer. This is quite reasonable,
though, and a far cry from your original "I'd like to be able to use any
well-defined arithmetic type and have the compiler tell me if it can do it
or not".
Most of the C programs I see have portability grafted on thru very
clever sets of

#ifdef HP_UX || AIX_1.0
#endif

I may be way iconoclastic, but I don't consider that "portability".
Three issues usually are at play here:

- Programmers who wouldn't know portability if, to paraphrase Blackadder,
"it painted itself purple and danced naked on a harpsichord singing
'portability is here again'". That is, gratuitously assuming things the
standard doesn't guarantee because it makes things "easier", when perfectly
portable alternatives exist; or refusing to write a single portable
implementation but instead focusing on individual platforms because it's
"faster". This is especially true in the case of numerical types; having
platform tests for those is just plain stupid.

- Needing functionality that is not defined by the standard, but available
on many or most target platforms; or being able to use optional
functionality that is available on some platforms, but not all. For
UNIX-like systems, you can hope the POSIX standard has what you need, but if
not, you may need to interface with different and incompatible interfaces to
system routines.

- Workarounds. What you're doing ought to work according to the standard(s),
but it doesn't on some platform, and you do not have the option of telling
your customers to get their platform fixed and accept a broken program in
the meantime.

We can dismiss case #1. For case #2, detecting what is possible at runtime
is usually not an option, nor is roll-your-own. The proper approach is to
sequester all platform-dependent functionality behind unified interfaces and
use those. This is the radical concept of a *library*, often eschewed out of
sheer laziness or out of a mistaken conviction that directly using a
specific interface is faster than defining and translating to a generic
interface, if such is required. (It may be, but whether the runtime cost of
this actually matters compared to the maintenance cost is almost never
considered.)

Whatever mechanisms are used to detect and enable the proper implementation
at compile time should be centralized in one place, preferably not in the
source at all, but the build process (this has its own drawbacks, but I
consider it a lot better than uglifying the source with "just one more
#ifdef"). If you must use #ifdef...#endif, though, it should ideally be
restricted to a few files that do nothing more than #include the proper
alternative.

Unfortunately, a combination of laziness, infatuation with the preprocessor
and being restricted to a spartan build system often win, and people will
squeeze 14 essentially different functions in one file, each contained in
its own #ifdef...#endif, or worse, there's just one function which has code
in a maze of #ifs, rendering it practically unreadable. The people who do
this will usually defend it with the observation that code is being shared
between the alternatives, which usually just means that they're not dividing
up their functions properly, or overestimating the usefulness of sharing
code between incompatible implementations in the first place.

FInally, for case #3, I'd consider an incidental #ifdef acceptable, if the
#ifdef focused on what workaround is being enabled, not what platform is
being compiled on. E.g. "#ifdef USE_MEMFROB_RETURNS_LONG_WORKAROUND", not
"#if defined(WIN32) && WIN32_VERSION > 3 && PHASE_OF_MOON != WANING".

S.
 
S

Skarmander

Al said:
Perhaps, but it also allows a style where such checks are done only
when necessary.
I considered this, but we're talking an optimization for which a compiler
may be very proficient at eliminating unnecessary checks, while the
programmer may be very proficient at forgetting a necessary check. If the
range is truly part of the type's semantics, and not just something to be
checked at module entry/exit, then not checking should be the exception
rather than the rule.

In the C spirit, ranged types could be toggled as entirely unchecked if
desired, leaving the range only as an aid to humans (and a way for static
checking tools and compilers to flag obviously incorrect statements).
Unfortunately, also in the C spirit, this would mean that ranged types would
almost never be checked in the first place.

Ultimately, though, it's probably not worth it adding such types to C
retroactively, so the point is moot. "If you want Pascal..."

S.
 
I

Ian Collins

Ancient_Hacker said:
<soap>
I think it's high time a language has the ability to do the very basic
and simple things programmers need to write portable software: the
ability to specify, unambiguously, what range of values they need to
represent, preferably in decimal, binary, floating, fixed, and even
arbitrary bignum formats. Not to mention hardware-dependent perhaps
bit widths. There's no need for the compiler to be able to actually
*do* any arbitrarily difficult arithmetic, but at least give the
programmer the ability to ASK and if the compiler is capable, and get
DEPENDABLE math. I don't think this is asking too much.
Isn't this a case of if the cap doesn't fit, use another one?

You could achieve what you describe in a language that supports operator
overloading on user defined types.
The biness of C having wink-wink recognized defacto binary integer
widths is IMHO just way below contempt. The way was shown many
different times since 1958, why can't we get something usable, portable
and reasonable now quite a ways into the 21st century?
Probably because the native platform ABI's are inconsistent in their types?
 
K

Keith Thompson

Skarmander said:
I considered this, but we're talking an optimization for which a
compiler may be very proficient at eliminating unnecessary checks,
while the programmer may be very proficient at forgetting a necessary
check. If the range is truly part of the type's semantics, and not
just something to be checked at module entry/exit, then not checking
should be the exception rather than the rule.

In the C spirit, ranged types could be toggled as entirely unchecked
if desired, leaving the range only as an aid to humans (and a way for
static checking tools and compilers to flag obviously incorrect
statements). Unfortunately, also in the C spirit, this would mean that
ranged types would almost never be checked in the first place.

Here's a thought.

Defined a new type declaration syntax:

signed(expr1, expr2) is a signed integer type that can hold values
in the range expr1 to expr2, both of which are integer constant
expressions. This will simply refer to some existing predefined
integer type; for example, signed(-32768, 32767) might just mean
short. The resulting type isn't necessarily distinct from any
other type.

unsigned(expr1, expr2): as above, but unsigned.

If an expression of one of these types yields a result outside the
declared bounds, the behavior is undefined. (Or it's
implementation-defined, chosen from some set of possibilities.)

All an implementation *has* to do is map each of these declarations to
some predefined type that meets the requirements. For example, if I
do this:

signed(-10, 10) x;
x = 7;
x *= 2;

I might get the same code as if I had written:

int x;
x = 7;
x *= 2;

But a compiler is *allowed* to perform range-checking.

The effort to implement this correctly would be minimal, but it would
allow for the possibility of full range-checking for integer
operations.

Any thoughts?
 
R

Richard Bos

Ancient_Hacker said:
!! ewwww. yuck. Somehow after I took one look at Ada, I just put it
out of my mind.

And yet, as Dik demonstrated, Ada is exactly what you asked for, in your
usual high-horsey C-is-too-primitive-to-be-believed way. If you
preachers won't accept your own paradise, how are we poor doomed, evil C
programmers supposed to trust you?

Richard
 
J

jacob navia

Skarmander said:
C cannot do this natively, so you'll need libraries. Luckily, C also
makes it possible to implement such libraries efficiently. This is a
good way of highlighting the differences in philosophy.

using a signed 64 bit integer type and working in cents
should be able to handle money quantities up to

92 233 720 368 547 758 US$ and 8 cents.

Enough to accomodate the now considerable total US
debt... :)

Using native types is quite easy considering the progres in
hardware in the last years. Besides, accounting people that
need those extreme money amounts will not shudder to buy a
latest model PC for a few thousand dollars.

You can work in decimals of cents if you need sensible rounding.
 
J

jacob navia

Ian said:
Isn't this a case of if the cap doesn't fit, use another one?

You could achieve what you describe in a language that supports operator
overloading on user defined types.

Yes. That is why lcc-win32 proposes to enhance the language with that
feature. It is needed in MANY situations and it doesn't complexify
the language at all.

operator overloading is a MUST for C.

P.S. flames >/dev/null
 
H

Hallvard B Furuseth

Keith said:
Here's a thought.

Defined a new type declaration syntax:

signed(expr1, expr2) is a signed integer type that can hold values
in the range expr1 to expr2, both of which are integer constant
expressions. This will simply refer to some existing predefined
integer type;
(..snip..)

Go ahead and write up a proposal:) But check out the SBEIR proposal
first:

Specification-Based Extended Integer Range, Revision 3 (5.1)
(WG14/N459 X3J11/95-060) date 1995-08-25, by Farance & Rugolsky
http://wwwold.dkuug.dk/JTC1/SC22/WG14/docs/c9x/extended-integers/

More ambitious - maybe too ambitious, and with a somewhat different
goal. Turned out to be a lot of tricky issues involved. There was a
lot of discussion about it on comp.std.c and (I think) in the C standard
committee, but in the end it was left out. Don't remember why - too
late and too hairy, maybe.
 
A

av

The funny thing is this issue was partly solved in 1958, 1964, and in
1971.

In 1958 Grace Hopper and Co. designed COBOL so you could actually
declare variables and their allowed range of values! IIRC something
like:

001 DECLARE MYPAY PACKED-DECIMAL PICTURE "999999999999V999"
001 DECLARE MYPAY USAGE IS COMPUTATIONAL-/1/2/3

Miracle! A variable with predictable and reliable bounds! Zounds!

in how i see the thing: *for doing all*

1) it is need a routine for convalidate input range allow
2) it is need a fixed float number type (that has its pricision
set by programmer or user for each program) that can calculate all
until there is some memory in the system (never overflow)
 
S

Skarmander

jacob said:
Yes. That is why lcc-win32 proposes to enhance the language with that
feature. It is needed in MANY situations and it doesn't complexify
the language at all.
The canonical word is "complify", by analogy with "simplify".
operator overloading is a MUST for C.
Meh. Discussions on the pros and cons of operator overloading are plenty,
but I hardly see why C of all languages would need it so badly. C has
limited support for user-defined types to begin with; I'd hate to see
operator overloading be introduced as a crutch.

For numerical applications, where operator overloading would come in most
naturally, C has always been a bit of a specialist; fast but requiring some
care. Syntactic sugar is of less concern to those applications than portable
and fast calculations are. You'll effectively achieve that people will be
able to write "d = a * b + c" rather than "d =
xfloat128_add(xfloat128_mult(a, b), c)", which is nice but not spectacular.
Readability of the individual calculations is usually not high on the
priority list.

If you want wins in this area, introduce genericity. It does for semantics
what overloading does for syntax. C currently doesn't support generic
functions well; implementations that use macros or void* are clearly
problematic. Of course, don't go hog-wild crazy with the concept, like C++
did -- templates, overloading and implicit conversions all conspire there to
form name resolution semantics that are full of gotchas.
P.S. flames >/dev/null

Well, there's no point arguing with an opinion given without justification
anyway.

S.
 
E

ena8t8si

Al said:
Perhaps, but it also allows a style where such checks are done only
when necessary.

So does Ada, or Cobol, or most any other programming
language. Just an observation.
 
E

ena8t8si

Keith said:
Here's a thought.

Defined a new type declaration syntax:

signed(expr1, expr2) is a signed integer type that can hold values
in the range expr1 to expr2, both of which are integer constant
expressions. This will simply refer to some existing predefined
integer type; for example, signed(-32768, 32767) might just mean
short. The resulting type isn't necessarily distinct from any
other type.

unsigned(expr1, expr2): as above, but unsigned.

If an expression of one of these types yields a result outside the
declared bounds, the behavior is undefined. (Or it's
implementation-defined, chosen from some set of possibilities.)

All an implementation *has* to do is map each of these declarations to
some predefined type that meets the requirements. For example, if I
do this:

signed(-10, 10) x;
x = 7;
x *= 2;

I might get the same code as if I had written:

int x;
x = 7;
x *= 2;

But a compiler is *allowed* to perform range-checking.

The effort to implement this correctly would be minimal, but it would
allow for the possibility of full range-checking for integer
operations.

Any thoughts?

If you think about the ramifications of &x and what
it means to have pointers/arrays for these things,
you'll probably find it's a much bigger language
change than it first appears. Not to say it can't
be done, but it is a big change.
 
J

jacob navia

Skarmander said:
Meh. Discussions on the pros and cons of operator overloading are
plenty, but I hardly see why C of all languages would need it so badly.
C has limited support for user-defined types to begin with; I'd hate to
see operator overloading be introduced as a crutch.

For numerical applications, where operator overloading would come in
most naturally, C has always been a bit of a specialist; fast but
requiring some care. Syntactic sugar is of less concern to those
applications than portable and fast calculations are. You'll
effectively achieve that people will be able to write "d = a * b + c"
rather than "d = xfloat128_add(xfloat128_mult(a, b), c)", which is nice
but not spectacular. Readability of the individual calculations is
usually not high on the priority list.

Look, if you have
double a,b,c,d;
...
d = a*b+c;

you need more precision? You do:
qfloat a,b,c,d;
...
d = a*b+c;

You see?
Overloaded operators make your code more PORTABLE! You do not have to
rewrite it for the new type but you can use the same algorithms with
the new numeric type WITHOUT REWRITING EVERYTHING.

d = xfloat128_add(xfloat128_mult(a, b), c);

Doing this is HORRIBLE (I have done it several times)
 
S

Skarmander

jacob said:
Look, if you have
double a,b,c,d;
...
d = a*b+c;

you need more precision? You do:
qfloat a,b,c,d;
...
d = a*b+c;

You see?

Like I said, nice but not spectacular. You won't need search & replace as
much if you decide to switch types. Current C programs achieve this mostly
through typedefs and macros, which is only marginally worse.
Overloaded operators make your code more PORTABLE! You do not have to
rewrite it for the new type but you can use the same algorithms with
the new numeric type WITHOUT REWRITING EVERYTHING.
Poor man's polymorphism. This sort of thing works much better in a language
with a unified type system, where you can actually create new integer subtypes.
d = xfloat128_add(xfloat128_mult(a, b), c);

Doing this is HORRIBLE (I have done it several times)
I'm not saying operator overloading is useless, but I do contend it's not as
impressive as programmers seem to think. It frees you from typing (as in
keypresses, not as in types) but wants you to think carefully about what
function could be invoked with every expression you write down, lest nasty
surprises befall you. The lossage it causes in combination with promotions
is notorious in C++.

S.
 
K

Keith Thompson

Hallvard B Furuseth said:
Go ahead and write up a proposal:) But check out the SBEIR proposal
first:

Specification-Based Extended Integer Range, Revision 3 (5.1)
(WG14/N459 X3J11/95-060) date 1995-08-25, by Farance & Rugolsky
http://wwwold.dkuug.dk/JTC1/SC22/WG14/docs/c9x/extended-integers/

More ambitious - maybe too ambitious, and with a somewhat different
goal. Turned out to be a lot of tricky issues involved. There was a
lot of discussion about it on comp.std.c and (I think) in the C standard
committee, but in the end it was left out. Don't remember why - too
late and too hairy, maybe.

Looks like I don't need to write up a proposal. :cool:}

C99's <stdint.h> is, IMHO, a great improvement over C90's, well, lack
of <stdint.h>, but the SBEIR proposal is much more general. I like
it.

One suggestion I would have made is to allow a specification of the
required range of a type, rather than just the number of bits. Bit
counts, IMHO, place too much emphasis on the representation of a type
rather than what it's used for. Sometimes I just want to know how
many widgets I can count, not how many bits the computer will use to
count them. Of course, sometimes the representation is important; I'd
advocate ranges *in addition to* the number of bits, not as a
replacement.

Another thing: the syntax for suffixed literals is becoming unwieldy
in C, and more so in the SBEIR proposal. For example, SBEIR proposes
that 456P32U specifies an unsigned 32-bit constant with the value 456.
(I would at least allow an underscore: 456_P32U.) The problem is that
there's an extremely terse syntax that parallels the syntax of integer
type names. Instead, I would have suggested using the actual type
name to specify the type of an integer literal. One way to do this is
to use cast syntax: in C99 <stdint.h> terms, (uint32_t)456. In actual
C99, the type of the literal is determined purely by the literal
itself (int in this case), and the cast specifies a conversion to the
specified type. Instead, a special rule could say that when a numeric
literal is the immediate operand of a cast to a numeric type, the
literal has the specified type rather than the default one. Or a
different syntax could have been introduced (Ada, for example,
distinguishes between conversions and qualified expressions).

Of course, the existing suffixes would still have to be supported.

To get back to some semblance of topicality, I wonder how much of this
stuff could be done within standard C. Certainly a compiler could
provide it as an extension, but that doesn't help anyone trying to
write portable code.

Is it possible to write a macro such that
UNSIGNED_TYPE(1000000)
portably expands to either "unsigned int" or "unsigned long",
depending on which type can hold the specified value? I suspect not.
 
F

Frederick Gotham

Skarmander posted:
Like I said, nice but not spectacular. You won't need search & replace
as much if you decide to switch types. Current C programs achieve this
mostly through typedefs and macros, which is only marginally worse.


Are you familiar with C++ at all? If so, you'd know that classes and
templates are far more than simple macro and typedef substitution.

It frees you from typing (as in keypresses, not as in types) but wants
you to think carefully about what function could be invoked with every
expression you write down, lest nasty surprises befall you. The lossage
it causes in combination with promotions is notorious in C++.


Operator overloading allows you interact with user-defined types in the
same way as you would interact with an intrinisc type, e.g.:

string str("Hello");

str += " world";

Templates allow you to write one function (or struct, or class), whose code
the compiler can re-use to make a function which deals with different
types.

Put them together and you've got a very powerful tool. A sample usage might
be to write a class called "Int1024" which is a 1024-Bit integer, and to
use operator overloading to make it behave like a built-in integer, so that
we can simply do:

Int1024 i = 56;

i *= 3;

Then we could write a template function which can work with any kind of
integer type, be it built-in or user-defined:

template<typename T>
void DoSomething(void)
{
T obj = 8;

obj *= 5;
}

int main(void)
{
DoSomething<int>();

DoSomething<Int1024>();
}
 

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,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top