why still use C?

M

Mike Cowlishaw

This has already been rehashed to death. Use an appropriate scaling
factor and all these computations can be performed exactly using
binary floating point, or, even better, long long arithmetic. Before
C99, the
usual portable solution was double precision, but 64-bit integer
arithmetic is even more appropriate to this kind of applications.
Maybe
even more appropriate than decimal floating point arithmetic
(depending
on the size of the mantissa and the scaling factor imposed by the
application).

This is, indeed, how decimal arithmetic has been done in the
past. It is no longer an adequate or acceptable approach.

It's a valid approach for binary FP, too, and 'manual'
scaling of binary calculations is perfectly feasible.
But it is difficult, tedious, and error-prone.

In addition to these problems, the 'scaled binary'
approach for decimal means that one is constantly
carrying out base conversions. With decimal FP
as the foundation, no base conversions occur.

Mike Cowlishaw
 
M

Mike Cowlishaw

With SMT, processors are going the other way. One floating point unit
used by two processors. The additional logic to generate a decimal
ALU relative to a binary ALU is pretty small. As I understand the
proposals, they store numbers in memory with each three digits stored
into 10 bits, though in registers they are BCD.

Pretty close ... registers would normally hold the numbers in the
compressed format (so they stay 64-bit, etc., and can
be shared with the BFP unit). These can then be expanded to
BCD within the DFP unit to carry out arithmetic -- this only
costs 2-3 gate delays. [Other approaches are possible.]
I will guess that it increases the logic between 10.000% and 20.000%.

Sounds about right.

Mike Cowlishaw
 
B

Brian Inglis

This has already been rehashed to death. Use an appropriate scaling
factor and all these computations can be performed exactly using binary
floating point, or, even better, long long arithmetic. Before C99, the
usual portable solution was double precision, but 64-bit integer
arithmetic is even more appropriate to this kind of applications. Maybe
even more appropriate than decimal floating point arithmetic (depending
on the size of the mantissa and the scaling factor imposed by the
application).

I'm not at all sure that the financial community would be interested
in decimal floating point rather than fixed point arithmetic.
Unless extended precision is available, you can handle less than a
billion to six decimals.
I've done back of envelope calculations to ensure that numbers that
had to add up exactly would not lose precision in intermediate values
used within companies.
A safer range for most transactional work would be billions to six
decimals, requiring sixteen digits, and another three digits for
safety if you're a multinational company or federal government.
If you're converting between currencies with six decimals accuracy,
then you need to add another six decimals onto the end for exactness.
The total range tends not to change by currency; lire, won, yen, yuan
are like cents or pennies in other currencies: the decimal place comes
after them instead of before them; and if you're dealing in larger
aggregates, you often don't need the precision to more than one or a
thousand currency units.
Don't know much about countries' internal economies: would be
interesting to know how big the numbers are required to be for India
and China, whose populations are three to four times bigger than the
EU, Indonesia, US?
 
C

Chris Hills

Dan Pop <[email protected]> said:
A small percentage that would be better used as an additional binary
floating point execution unit.

I think in real terms, weather we like it or not this is pointless
argument.

IBM will produce the CPU with decimal FP
Compiler vendors will produce compiler to support IBM's FP
Other Silicon vendors are likely to put Decimal FP on some of their CPU
Other compiler vendors will support them.

I suggest that we stop the arguing about if it is a good idea as it is
going to happen anyway. What we have to do is make sure that the API is
sensible and in the C and C++ standards so that the compiler vendors all
do (at least the core) support for FP10 the same way.

I would suggest that this is one place where it would be sensible to
have, at least the core, the same for C and C++


/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
\/\/\/\/\ Chris Hills Staffs England /\/\/\/\/\
/\/\/ (e-mail address removed) www.phaedsys.org \/\/
\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
 
B

Brian Inglis

Why is such conformance (up to the last digit) important?

Transactional systems have to get the numbers exact: for example, when
companies or banks are settling accounts between themselves, and a
single cheque is written (or EFT generated) to settle each day's or
month's accounts, that cheque must be for exactly the same number as
if each transaction was settled by a separate cheque (or EFT).

Adding machines / calculators are still used to spot check / audit
results from systems and the results are expected to be exactly the
same. The bean counters get very upset if a result from a new system
run on old data is not exactly the the same as the result they got
from the old system.

I've been on projects where we worked for a month on a penny
discrepancy -- the final result on the new system was out by a penny
from the old system -- but every intermediate value we checked was
identical, and the final total was being calculated correctly without
any loss of precision (in binary FP) -- the systems project lead gave
the business project lead a penny, and he signed off on the system at
last.
 
G

glen herrmannsfeldt

PL/I was moderately successful; for example MULTICS was
implemented in PL/I, and a lot of IBM systems programming
was done in PL/I.

PL/I failed to do what IBM wanted, which was to replace Fortran
and COBOL. I believe that there was once a plan not to write
Fortran or COBOL compilers for S/360.

Though around that time it was not obvious that S/360 would be
the success it turned out to be.

I am pretty sure that no-one at the time expected S/360 software
to still run on computers build 40 years later. I have recently
seen reports of testing the S/360 PL/I and Fortran compilers on
OS/390 and z/OS on current machines. They do still run!

PL/I was over ambitious for the machines that existed at the time.

-- glen
 
G

glen herrmannsfeldt

CBFalconer said:
glen herrmannsfeldt wrote: (snip)
Think about how you would normalize such a value. That would not
be a decimal format, that would be a base 1000 format. The count
of significant digits would jitter over a range of 3!

The normalization is done in uncompressed (BCD) form, and then
they are converted to the base 1000 form for storage.

So 50 bits would store 15 decimal digits, so 54 would store 16.
With sign, that would leave nine for an exponent, which would allow
an exponent between -128 and +127. (Some exponent values may be
reserved, though.) 16 decimal digits hold between 49.8 and 53.1 bits
A decimal 9 bit exponent representing 10**-256 though 10**255 is
equivalent to 10.7 bits of binary exponent.

So, sign+exponent+mantissa it is equivalent to 1+49.8+10.7=61.5 bits,
or 61.5/64.0=96% efficient in the worst case, and 64.8/64.0=100.1% in
the best case. About 98% on average.

-- glen
 
C

Christian Bau

glen herrmannsfeldt said:
The normalization is done in uncompressed (BCD) form, and then
they are converted to the base 1000 form for storage.

So 50 bits would store 15 decimal digits, so 54 would store 16.
With sign, that would leave nine for an exponent, which would allow
an exponent between -128 and +127. (Some exponent values may be
reserved, though.) 16 decimal digits hold between 49.8 and 53.1 bits
A decimal 9 bit exponent representing 10**-256 though 10**255 is
equivalent to 10.7 bits of binary exponent.

So, sign+exponent+mantissa it is equivalent to 1+49.8+10.7=61.5 bits,
or 61.5/64.0=96% efficient in the worst case, and 64.8/64.0=100.1% in
the best case. About 98% on average.

The proposed format for decimal numbers is just very slightly more
efficient: The 64 bit format uses indeed 50 bits for 15 decimal digits.
Then it uses 5 bits which can hold 32 values to encode one decimal
digits, three values (-1, 0, 1) that will become part of the exponent,
and two values encode infinity and NaN.

One bit is used for the sign, 8 more bits for the decimal exponent, so
you have 768 values for the exponent from -384 to +383.
 
M

Mike Cowlishaw

With SMT, processors are going the other way. One floating point
Think about how you would normalize such a value. That would not
be a decimal format, that would be a base 1000 format. The count
of significant digits would jitter over a range of 3!

Well first of all, why would one normalize? [See the FAQ at
http://www2.hursley.ibm.com/decimal/decifaq.html part 4,
for a discussion.]

Also, the base 1000 is used only for storage; all computations,
rounding, etc., are carried out in base 10.

Mike Cowlishaw
 
C

CBFalconer

glen said:
The normalization is done in uncompressed (BCD) form, and then
they are converted to the base 1000 form for storage.

If you have ever designed a floating point package you will
realize that normalization takes up the majority of the time. It
needs to be simple, not a major base conversion.

Any reasonable form of decimal FP will be based on some flavor of
bcd, possibly 8421, or excess 3, or 2*421, or even bi-quinary.

USE worldnet address!
 
F

Fergus Henderson

While we are at it, let us do the same for integer arithmetic.
That way, we need never again see "I think my compiler has a bug. My
program keeps giving wrong answers. It says that 5/3 equals 1."

Good idea. Many programming languages have adopted this idea,
using a different operator than "/" for truncating integer division.
 
G

Gene Wirchenko

Fergus Henderson said:
Good idea. Many programming languages have adopted this idea,
using a different operator than "/" for truncating integer division.

And here I thought that "/" was the different operator, used
instead of

*

*****

*

Sincerely,

Gene Wirchenko





Computerese Irregular Verb Conjugation:
I have preferences.
You have biases.
He/She has prejudices.
 
G

glen herrmannsfeldt

Christian said:
The proposed format for decimal numbers is just very slightly more
efficient: The 64 bit format uses indeed 50 bits for 15 decimal digits.
Then it uses 5 bits which can hold 32 values to encode one decimal
digits, three values (-1, 0, 1) that will become part of the exponent,
and two values encode infinity and NaN.

One bit is used for the sign, 8 more bits for the decimal exponent, so
you have 768 values for the exponent from -384 to +383.

I had remembered the three digits in 10 bits form. The rest was just
a guess as to how it possibly could be done.

OK, so still between 49.8 and 53.1 equivalent bits for the mantissa,
11.3 bits for the exponent, and 1 for the sign, so 62.1 worst case
and 65.4 best case, for 63.75 average case, or 99.6% efficient.

This reminds me of all the work the authors of the original Fortran
compiler did to generate as efficient object code as possible.
They needed to convince people that Fortran could be used in place
of assembly code, without a significant loss of efficiency.

Personally, I would have been happy with ordinary BCD arithmetic,
in floating point form. It seems to have worked well for calculator
users for many years now.

-- glen
 
G

glen herrmannsfeldt

Gene said:
And here I thought that "/" was the different operator, used
instead of

*

*****

*


The calculator in Microsoft windows has a / on the divide key.

Some time ago, my son was asking how to input fractions into
the calculator, and I showed him the key used to do it.

1 / 3 = and you get one third.

(He already knew that it was the divide key.)

-- glen
 
G

glen herrmannsfeldt

CBFalconer said:
glen herrmannsfeldt wrote:

(snip of base 1000 representation of decimal floating point)
If you have ever designed a floating point package you will
realize that normalization takes up the majority of the time. It
needs to be simple, not a major base conversion.
Any reasonable form of decimal FP will be based on some flavor of
bcd, possibly 8421, or excess 3, or 2*421, or even bi-quinary.

I haven't actually checked, but rumors are that it only takes a
few gates to convert between the base 1000 representation, and
BCD. It can be done while loading into registers, or even
as part of the ALU, itself.

-- glen
 
K

Kalle Olavi Niemitalo

Arthur J. O'Dwyer said:
Note that this code is not very general; it doesn't work for e.g.

CHECK_EXPR_TYPE(foo, int(*)[5]);

How about this:

#define CHECK_EXPR_TYPE(expr, type) \
((void) sizeof(((int (*)(type)) 0)(expr)))

I prefer writing it without the (void) though, so that I can use
the macro as part of a constant expression that initializes a
structure:

enum type {
T_NULL,
T_INT,
T_FLOAT
};
struct smember {
enum type type;
const char *name;
size_t offset;
};
#define SMEMBER_FLOAT(stype, member) \
{ T_FLOAT, #member, \
offsetof(stype, member) \
+ 0*CHECK_EXPR_TYPE(&((stype *) 0)->member, const float *) }

struct demo {
float girth;
float curvature;
};
const struct smember demo_members[] = {
SMEMBER_FLOAT(struct demo, girth),
SMEMBER_FLOAT(struct demo, curvature),
{ T_NULL }
};

Type checks like this would be easier if the comma operator were
allowed in constant expressions.
 
A

Arthur J. O'Dwyer

Arthur J. O'Dwyer said:
Note that this code is not very general; it doesn't work for e.g.

CHECK_EXPR_TYPE(foo, int(*)[5]);

How about this:

#define CHECK_EXPR_TYPE(expr, type) \
((void) sizeof(((int (*)(type)) 0)(expr)))

Quite ingenious, I think -- but AFAIK it isn't guaranteed to
be portable. What you're doing, step-by-step, is:

(int (*)(type)) cast something to ptr-to-function-taking-'type'
0 in fact, cast NULL to that ptr-to-func-type
(expr) see if it will accept 'expr' as an argument
sizeof but don't actually evaluate the result
(void) and discard the result of 'sizeof' as well

The problem is that it's not guaranteed possible to "call" a null
pointer, no matter what type it is, no matter what arguments you try
to pass it. So you have undefined behavior *inside* the 'sizeof',
even though it will not be evaluated. As far as I know. Chapter
and verse would be welcome.
Similar problems shoot down this macro:

#define my_broken_offsetof(t, f) ((char *)&((t *)0)->f - (char *)0)

and I'm curious as to what various compilers make of 'sizeof(1/0)',
and whether it's valid C or not. [I think the answer should be
sizeof(int), but I'm not sure it doesn't invoke UB.]

-Arthur
 
P

pete

Arthur J. O'Dwyer wrote:
and I'm curious as to what various compilers make of 'sizeof(1/0)',
and whether it's valid C or not.
[I think the answer should be
sizeof(int),

It is.
but I'm not sure it doesn't invoke UB.]

It doesn't.
The division by zero, does not occur.
 
F

Fergus Henderson

Arthur J. O'Dwyer said:
Arthur J. O'Dwyer said:
Note that this code is not very general; it doesn't work for e.g.

CHECK_EXPR_TYPE(foo, int(*)[5]);

How about this:

#define CHECK_EXPR_TYPE(expr, type) \
((void) sizeof(((int (*)(type)) 0)(expr)))

Quite ingenious, I think -- but AFAIK it isn't guaranteed to
be portable. What you're doing, step-by-step, is:

(int (*)(type)) cast something to ptr-to-function-taking-'type'
0 in fact, cast NULL to that ptr-to-func-type
(expr) see if it will accept 'expr' as an argument
sizeof but don't actually evaluate the result
(void) and discard the result of 'sizeof' as well

The problem is that it's not guaranteed possible to "call" a null
pointer, no matter what type it is, no matter what arguments you try
to pass it.

That's OK; this code doesn't call a null pointer. It contains a
subexpression which is a call to a null pointer, but that subexpression
is never evaluated. Undefined behaviour would only result if that
subexpression was evaluated.
Similar problems shoot down this macro:

#define my_broken_offsetof(t, f) ((char *)&((t *)0)->f - (char *)0)

As long as you never invoke that macro, there's no problem.

If however, you invoke it, e.g.

#define my_broken_offsetof(t, f) ((char *)&((t *)0)->f - (char *)0)
struct s { int x; };
int main() {
return my_broken_offsetof(struct s, x);
}

then the subexpression "((t *)0)->f", which in this example becomes
"((struct s *)0)->x" after macro expansion, will get evaluated,
and so the behaviour is undefined.
 
C

CBFalconer

glen said:
(snip of base 1000 representation of decimal floating point)




I haven't actually checked, but rumors are that it only takes a
few gates to convert between the base 1000 representation, and
BCD. It can be done while loading into registers, or even
as part of the ALU, itself.

If you show me (in detail) I will believe you. Not before.

USE worldnet address!
 

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

Similar Threads


Staff online

Members online

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,152
Latest member
LorettaGur
Top