#define and preprocessor magic

M

Marcin Lukasik

Hello,

I have the following in my code:
#define _TST {100, 200, 300, 400}

How do I extract any of four elements from that define, and put it into another define? So I would like to have something like this:

#define VALUE2 _TST[2]
(but this of course won't work)

Is there a way of doing it without declaring an array and assigning _TST to it?

Thank you in advance,
Marcin
 
K

Keith Thompson

Marcin Lukasik said:
I have the following in my code:
#define _TST {100, 200, 300, 400}

How do I extract any of four elements from that define, and put it
into another define? So I would like to have something like this:

#define VALUE2 _TST[2]
(but this of course won't work)

Is there a way of doing it without declaring an array and assigning
_TST to it?

Assuming your compiler supports compound literals, this works for me:

#include <stdio.h>

#define TST {100, 200, 300, 400}
#define VALUE(i) ((int[])TST)

int main(void) {
for (int i = 0; i < 4; i ++) {
printf("VALUE(%d) = %d\n", i, VALUE(i));
}
return 0;
}

But is there some reason you need that macro? It would probably make
more sense to define an object and initialize it with the desired
values:

#include <stdio.h>

const int TST[] = {100, 200, 300, 400};

int main(void) {
for (int i = 0; i < 4; i ++) {
printf("TST[%d] = %d\n", i, TST);
}
return 0;
}

Incidentally, identifiers starting with an underscore and an uppercase
letter are reserved to the implementation; you should never define such
identifiers in your own code.
 
M

Marcin Lukasik

Assuming your compiler supports compound literals, this works for me:



#include <stdio.h>



#define TST {100, 200, 300, 400}

#define VALUE(i) ((int[])TST)


Thank you, this does the trick!

But is there some reason you need that macro? It would probably make

more sense to define an object and initialize it with the desired

values:

You're 100% right and I will do this eventually. I didn't write the code myself, many people have contributed and we ended up with the same variables being used twice, but declared differently... It's a temporary fix.


One last question... when I change the type to float, and do:
#define TST {100.01, 200.002, 300.03, 400.04}
#define VALUE(i) ((float[]) TST)

printf("VALUE(2): %f\n", VALUE(2)) returns "VALUE(2): 300.029999".
Why?

I'm using gcc, I only use -Wall flag.

Thanks,
Marcin
 
B

Ben Bacarisse

Marcin Lukasik said:
One last question... when I change the type to float, and do:
#define TST {100.01, 200.002, 300.03, 400.04}
#define VALUE(i) ((float[]) TST)

printf("VALUE(2): %f\n", VALUE(2)) returns "VALUE(2): 300.029999".
Why?


There are many numbers that can't be represented exactly when stored as
floating point objects. Exactly which ones depends on the floating
point representation used, but 300.03 can't be represented exactly using
binary floating point.

The main benefit of using floating point is to gain range at the expense
of precision, but it is often used simply to have a way to represent
fractional numbers. When used for that purpose, the loss of precision
is just annoying, but C does not have an exact non-integer numeric type.
 
J

James Kuyper

Assuming your compiler supports compound literals, this works for me:



#include <stdio.h>



#define TST {100, 200, 300, 400}

#define VALUE(i) ((int[])TST)


Thank you, this does the trick!

But is there some reason you need that macro? It would probably make

more sense to define an object and initialize it with the desired

values:

You're 100% right and I will do this eventually. I didn't write the code myself, many people have contributed and we ended up with the same variables being used twice, but declared differently... It's a temporary fix.


One last question... when I change the type to float, and do:
#define TST {100.01, 200.002, 300.03, 400.04}
#define VALUE(i) ((float[]) TST)

printf("VALUE(2): %f\n", VALUE(2)) returns "VALUE(2): 300.029999".
Why?


The only numbers that can be represented exactly in double precision
floating point are those between DBL_MIN and DBL_MAX which are of the
form mantissa*pow(FLT_RADIX,exponent), where mantissa and exponent are
both integers, and mantissa must be less than FLT_RADIX/DBL_EPSILON.
DBL_MIN, DBL_MAX, FLT_RADIX, and DBL_EPSILON are all macros #defined in
<float.h>. 300.03 is 30003/100, which cannot be put into that form
unless FLT_RADIX is divisible by 10. On almost all machines, FLT_RADIX
is 2; on almost all of the other machines, FLT_RADIX is a power of 2,
usually 16.

Have a look at "What every computer scientist should know about floating
point arithmetic" by D. Goldberg. It's available on the web in various
formats. The first one that I was able to find was provided by Julian V.
Noble:
http://galileo.phys.virginia.edu/classes/551.jvn.fall01/
 
M

Marcin Lukasik

The main benefit of using floating point is to gain range at the expense
of precision, but it is often used simply to have a way to represent
fractional numbers. When used for that purpose, the loss of precision
is just annoying, but C does not have an exact non-integer numeric type.

Thank you Ben.
But does this mean that VALUE(2) stores 300.029999 or 300.03?
In other words do I loose precision by doing ((float[]) TST) or by formatting it as %f in printf?

Marcin
 
B

Ben Bacarisse

Marcin Lukasik said:
Thank you Ben.
But does this mean that VALUE(2) stores 300.029999 or 300.03?

Neither! It is stored as the closest floating point number to 300.03.
Exactly what that is is probably not important to you, but you can work
it out if you really need to know. Read the excellent paper that James
directed you to for all the details (in my opinion, somewhat more than
every programmer needs to know, but extra knowledge is never a problem).
In
other words do I loose precision by doing ((float[]) TST) or by
formatting it as %f in printf?


%f confuses the issue because using less precision for output can make
it seem more accurate. %.2f will print 300.03 because that's the
closest 2-digit decimal representation of the value that's stored
internally.

By the way, "modern" C has a format (%a) that lets you print out the
exact internal value but it won't be easy to understand what you get
until you have read more about floating point numbers.
 
E

Eric Sosman

The main benefit of using floating point is to gain range at the expense
of precision, but it is often used simply to have a way to represent
fractional numbers. When used for that purpose, the loss of precision
is just annoying, but C does not have an exact non-integer numeric type.

Thank you Ben.
But does this mean that VALUE(2) stores 300.029999 or 300.03?
In other words do I loose precision by doing ((float[]) TST) or by formatting it as %f in printf?


Both, probably. (Also, the word you want is "lose.")

Most systems nowadays store floating-point values in base two,
meaning that the number actually stored has the value N/D where
N and D are integers and D is a power of two. You can easily see
that no such N/D can be equal to 30003/100; it's exactly the same
problem familiar base-ten notation runs into with values like 22/7.

Most systems nowadays round the N of a `float' value to 24
base-two digits, just as you might round 22/7 to four base-ten
digits and get 3.143. Your `300.03' is probably 9831383/32768,
which is (if I haven't goofed) the nearest N/D such that N uses
only 24 bits and D is a power of two. The exact value of this
fraction is 300.029998779296875, closer than any other `float'
to the desired 300.03 but not exactly equal to it.

The "%f" conversion introduces a further approximation and
an additional error. Unadorned "%f" always prints six digits
after the decimal point, rounding the number as needed -- this
spares you from seeing horrors like "300.029998779296875". If
you know your numbers aren't good to six decimal places you can
use "%.2f" to get two places, or "%.5f" for five places, and so
on. Your `float' probably has 24 base-two digits, equivalent to
a little more than 7 decimal digits, so for values near 300 you
can't expect digits after the fourth decimal place to mean much.
If you were to round to four places with "%.4f" you'd probably
see a value you'd find less confusing.

Don't, by the way, make the mistake of thinking that because
a value is *stored* to seven-and-change decimal digits' precision
that it is *accurate* to that degree. If you weigh yourself in
pounds you'll probably get a three-digit number, so with seven
digits' precision you can tack on four decimal places, right?
How much faith should you put in that rightmost decimal place?
 
J

James Kuyper

The main benefit of using floating point is to gain range at the expense
of precision, but it is often used simply to have a way to represent
fractional numbers. When used for that purpose, the loss of precision
is just annoying, but C does not have an exact non-integer numeric type.

Thank you Ben.
But does this mean that VALUE(2) stores 300.029999 or 300.03?
In other words do I loose precision by doing ((float[]) TST) or by formatting it as %f in printf?


300.03 is a constant of type double, and on most systems cannot be
exactly represented in that type. For instance, on my system, the
compiler converts 300.03 into 1319545894726533*pow(2,-42), which is the
closest approximation it can get to that value using double precision
floating point. Precision is lost at the point of that conversion. When
you convert it float, additional precision is lost. Passing it to
printf() causes it to be converted back to double. This will generally
not lose you any additional precision, but it cannot restore the bits
that were lost by converting to float. Printing it with %f looses some
precision, but if you give it a floating point format with sufficiently
many significant digits, printf() can display the exact value that was
passed to it.
 
B

BartC

One last question... when I change the type to float, and do:
#define TST {100.01, 200.002, 300.03, 400.04}
#define VALUE(i) ((float[]) TST)

printf("VALUE(2): %f\n", VALUE(2)) returns "VALUE(2): 300.029999".
Why?


100.01 etc can't be exactly represented in binary floating as has been
explained.

If that .01 is important, store as a string ("100.01") or store as an
integer (10001) with an implied scale factor of 1/100.

(Depending on what the numbers will be used for, you might still get
problems when you eventually do arithmetic on these values if they are
converted to floating point. But you can sometimes work around such issues.)
 
K

Keith Thompson

Marcin Lukasik said:
One last question... when I change the type to float, and do:
#define TST {100.01, 200.002, 300.03, 400.04}
#define VALUE(i) ((float[]) TST)

[...]

Most numeric software in C uses type double rather than float.
It (usually) has substantially more precision, and on many systems
operations on double are not much more expensive than operations
on float.

If you need more precision than double, you can use long double --
but on some systems long double is no wider than double.

This won't avoid the issues discussed in the rest of this thread; it
will just push your errors a few more places past the decimal point.
 
P

Paul N

Hello,

I have the following in my code:
#define _TST {100, 200, 300, 400}

How do I extract any of four elements from that define, and put it into another define? So I would like to have something like this:

#define VALUE2 _TST[2]
(but this of course won't work)

Is there a way of doing it without declaring an array and assigning _TST to it?

Thank you in advance,
Marcin

I'm sure there must be some way of doing this (not that you actually
appear to need it, from your comments elsethread). The best I've
managed so far is:

#define f1(a, b, c, d) a
#define f2(a, b, c, d) b
#define f3(a, b, c, d) c
#define f4(a, b, c, d) d

#define _TST {100, 200, 300, 400}

#define g(v, x) f##x(v)

g(_TST, 2)

but this leaves in the brackets on the first and last items.
 
K

Keith Thompson

pete said:
Keith said:
Marcin Lukasik said:
One last question... when I change the type to float, and do:
#define TST {100.01, 200.002, 300.03, 400.04}
#define VALUE(i) ((float[]) TST)

[...]

Most numeric software in C uses type double rather than float.
It (usually) has substantially more precision, and on many systems
operations on double are not much more expensive than operations
on float.

If you need more precision than double, you can use long double --
but on some systems long double is no wider than double.

This won't avoid the issues discussed in the rest of this thread; it
will just push your errors a few more places past the decimal point.


I tend to think of double
as the more natural type than float,
because float promotes to double
in the default argument promotions.


Yes, but that's pretty much relevant only for variadic functions, mostly
the *printf() family. (It's also relevant for functions with old-style
declarations, but such functions are themselves largely irrelevant.)
 
G

glen herrmannsfeldt

Keith Thompson said:
Yes, but that's pretty much relevant only for variadic functions, mostly
the *printf() family. (It's also relevant for functions with old-style
declarations, but such functions are themselves largely irrelevant.)

I still remember early in the ANSI C days, upgrading to, I believe
the Microsoft C compiler 5.0, from the previous non-ANSI compiler.

Then, since I could, adding function prototypes and the program wouldn't
work anymore! I forget the exact combination, but it was related to the
promotion being different with and without prototypes.

-- glen
 
B

Ben Bacarisse

pete said:
Keith said:
Marcin Lukasik said:
One last question... when I change the type to float, and do:
#define TST {100.01, 200.002, 300.03, 400.04}
#define VALUE(i) ((float[]) TST)

[...]

Most numeric software in C uses type double rather than float.
It (usually) has substantially more precision, and on many systems
operations on double are not much more expensive than operations
on float.

If you need more precision than double, you can use long double --
but on some systems long double is no wider than double.

This won't avoid the issues discussed in the rest of this thread; it
will just push your errors a few more places past the decimal point.


I tend to think of double
as the more natural type than float,
because float promotes to double
in the default argument promotions.


That's one reason but another to me stronger reason is that unadorned
floating point constants are of type double. This, along with the
usual arithmetic conversions, means that floats often get converted to
double (if only conceptually) in expressions like VALUE(2) + 1.
 
H

hormelfree

Neither! It is stored as the closest floating point number to 300.03. Exactly
what that is is probably not important to you, but you can work it out if you
really need to know.

It's likely to have "5" as the last digit...
Read the excellent paper that James directed you to for
all the details (in my opinion, somewhat more than every programmer needs to
know, but extra knowledge is never a problem).

If by "every programmer", you mean somebody who might have any kind
of programming task thrown at them, then the knowledge is definitely
helpful, in at least you'll know why your financial application is
possibly giving wrong answers, but there's not enough information
there to actually proactively fix the problem, so just another busted
program, but you get paid the same amount anyway, so who cares except
the user...
 
K

Keith Thompson

Barry Schwarz said:
Not on any IEEE system where double is 8 bytes.

Oh?

As far as I can see, the last non-zero digit in the exact decimal
representation of a representable floating-point number is always
5 unless the number is an exact integer. (All such numbers are an
integer multiple of a power of 2.0.)

You can see this using printf() *if* you use enough digits *and*
the printf() implementation prints exact values.
 
J

James Kuyper

Not on any IEEE system where double is 8 bytes.

Could you explain that assertion?
IEEE double precision can only represent a value exactly if it can be
expressed as an integer times a power of 2. Such a value has a
fractional part only if that power is negative. If a fraction with a
denominator which is a power of two has any non-zero digits, the last
such digit is always 5.

As I've already mentioned, I've already determined that on my system
300.03 is approximated by 1319545894726533*pow(2,-42); therefore 42
digits after the decimal point is sufficient to show the last non-zero
digit:

~/testprog(52) cat three.c
#include <stdio.h>
#ifndef __STDC_IEC_559__
#error Does not conform to IEC 60559 specifications from annex F
#endif

int main(void)
{
printf("%zu, %.42f\n", sizeof 300.03, 300.03);
return 0;
}
~/testprog(53) gcc -std=c99 -pedantic -Wall -Wpointer-arith -Wcast-align
-Wstrict-prototypes -Wmissing-prototypes three.c -lm -o three
~/testprog(54) three
8, 300.029999999999972715158946812152862548828125
 
B

Barry Schwarz

Oh?

As far as I can see, the last non-zero digit in the exact decimal
representation of a representable floating-point number is always
5 unless the number is an exact integer. (All such numbers are an
integer multiple of a power of 2.0.)

You can see this using printf() *if* you use enough digits *and*
the printf() implementation prints exact values.

What you say is obviously true in the mathematical sense.

But in the practical sense, that doesn't seem to matter when trying to
use the values. With %30.20f, my system shows
300.02999999999997000000 and that remains unchanged even after
subtracting 2.8E-14 which should have had some effect on that 7.
Multiplying by 100 produces 30002.99999999999600000000.

Both serve to show that you cannot depend on the last non-zero digit
being well-behaved once you exceed DECIMAL_DIG.
 

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,733
Messages
2,569,440
Members
44,830
Latest member
ZADIva7383

Latest Threads

Top