C sucks at math? (help)

R

Richard Tobin

And that is also false. Finite but arbitrarily long bit strings give
you only a subset of the rational numbers.

Sorry, I didn't say what I meant. I *meant* to say that with finite
but unlimited strings of bits you can represent a countable set such
as the algebraic numbers, but if infinitely long strings are allowed
you can represent uncountable sets such as the reals. Obviously to
represent the algebraic numbers you would not be using a plain
positional notation.

-- Richard
 
F

Flash Gordon

Richard said:
Sorry, I didn't say what I meant. I *meant* to say that with finite
but unlimited strings of bits you can represent a countable set such
as the algebraic numbers, but if infinitely long strings are allowed
you can represent uncountable sets such as the reals. Obviously to
represent the algebraic numbers you would not be using a plain
positional notation.

Actually, this is wrong.

The set of integers and the set of rationals are both countable as shown
in http://web01.shu.edu/projects/reals/infinity/countble.html
The set of reals in not countable, as shown in
http://web01.shu.edu/projects/reals/infinity/uncntble.html

In fact, it is provable that the set of reals between 0 and 1 is larger
than the set of all integers. However, amusingly enough, the set of
reals between 0 and 1 is the same size as the set of all reals.

I attended a one hour lecture on infinity, and it was probably the most
amusing maths lecture I ever attended because is showed how completely
absurd maths can be whilst also being completely consistent and useful.

Whilst interesting, this is not really topical here. Have a look at
http://web01.shu.edu/projects/reals/index.html or any of the many other
sites that cover this.
 
D

David Wade

Jordi said:
I have made a little C program that generates the following output:
227.000000 / 5 = 45.400002

Here's the code:
int main (int argc, char* argv[]) {
float var = 227;
printf("%f / 5 = %f\n", var, var / 5);
return 0;
}

Obviously, the output should be 45.4 (or 45.400000), but for some
mysterious reason C decided to add 0.000002* to the result. The error
might seem minor and this program is obviously trivial, but I need to
do similar calculations in a much bigger project and the problem I'm
encountering is caused by exactly this error.

* It seems that it is not .000002 is added, but rather
.0000015258789076710855... (which I found out after subtracting 45.4
from the result and then multiplying it with 10,000,000,000,000,000)

Why does C do this and what can I do about it?

It does this type of thing because:-

1. It represents numbers not as decimal fractions but as binary fractions.
2. It only has a finit number of digits/bits

The effect is some what similar to the following:-

assume we are working in decimal to three digits. We have 1.0/3.0 = 0.33.
Multiply up again and 3*0.33=0.99.
Any help or tips are greatly appreciated!

Depends on what you are doing. If you can try and scale the problem so you
can work in integers.E.G. if its dollars and cents work in cents.
As others have said use "double" rather than float can help. Might help to
help the compiler. Saying things like "var/5.0" or even "var*0.5" (but watch
out because although "0.5" usually converts to an exact binary fraction,
"0.l" doesn't...) You might also need to learn how to use the constants in
<float.h> and <math.h> The "The Standard C Library" by Plauger has some
usefull stuff on these. The trouble is when you start using floating point
you may need to start to understand the both the maths behind the
calculations, and what really happens in the FP routines.
Its just too easy to subtract two similar sized numbers end up with residual
error as an answer....
 
D

David Wade

Gordon Burditt said:
There is no exact representation of most decimal numbers in binary
floating point. Float only claims to have 6 digits, and your output
is off by 2 counts in the 8th digit. You have little cause to complain
that you didn't get the accuracy advertised.

45.4 as float:
Before: 45.399997711181640625000000000000000000000000000000000000000000
45.400001525878906250000000000000000000000000000000000000000000
45.400005340576171875000000000000000000000000000000000000000000

The three choices are shown above. The value you got was the closest
possible representation of the mathematically correct answer in an
IEEE 32-bit floating point number.


There is no such number exactly representable in binary floating point.


Then use a more precise floating-point value, like double or long double.
(That's just putting off the problem, though).

Use floating-point hardware with an infinite number of bits. Or,
if that's too expensive, just use MORE bits.

Or switch to a language that uses decimal numbers, such as REXX, However
even REXX only allows a finite number of digits so you can still get into
problems....
 
M

Mark McIntyre

I have made a little C program that generates the following output:
227.000000 / 5 = 45.400002

This is a FAQ. Please read it, and then read Goldberg.

Hint: its nothing to do with C, its entirely to do with how computers
store floating point numbers.
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
D

Dik T. Winter

>
> Actually, this is wrong.

It is wrong, but not for the reason you mention. Indeed with finite strings
(when we consider them as binary representations) you can represent countable
sets, but *not* the algebraic numbers (although they are countable). Only a
subset of the rationals. With (countably) infinite long strings you can
represent all reals.
> The set of integers and the set of rationals are both countable as shown
> in http://web01.shu.edu/projects/reals/infinity/countble.html
> The set of reals in not countable, as shown in
> http://web01.shu.edu/projects/reals/infinity/uncntble.html

Indeed. But the set of algebraic numbers is also countable, as is the set
of computable numbers (the latter is the largest countable set of numbers
in some sense). I may note that the set of algebraic numbers is a proper
subset of the set of reals.
> In fact, it is provable that the set of reals between 0 and 1 is larger
> than the set of all integers. However, amusingly enough, the set of
> reals between 0 and 1 is the same size as the set of all reals.

And the set of even integers is the same size as the set of all integers.
And the set of integers of the form 2 ** n is the same size as the set of
all integers.
 
R

Richard Tobin

Sorry, I didn't say what I meant. I *meant* to say that with finite
but unlimited strings of bits you can represent a countable set such
as the algebraic numbers, but if infinitely long strings are allowed
you can represent uncountable sets such as the reals. Obviously to
represent the algebraic numbers you would not be using a plain
positional notation.
[/QUOTE]
Actually, this is wrong.
The set of integers and the set of rationals are both countable as shown
in http://web01.shu.edu/projects/reals/infinity/countble.html
The set of reals in not countable, as shown in
http://web01.shu.edu/projects/reals/infinity/uncntble.html

This is true, but I am at a loss as to what you think it contradicts
what I said.

-- Richard
 
R

Richard Tobin

It is wrong, but not for the reason you mention. Indeed with finite strings
(when we consider them as binary representations) you can represent countable
sets, but *not* the algebraic numbers (although they are countable).

That's why I said "not using a plain positional notation". You can
represent the algebraic numbers, and all computable numbers, using
finite sequences of bits.

For example, you could represent each algebraic number as a list of
the integer coefficients of a polynomial it satifisfies, along with an
integer indicating which root it is. You could then encode the
sequence of integers as a sequence of bits in whatever manner you
prefer. Of course, the arithmetic operations are non-trivial given
this representation.

-- Richard
 
K

Keith Thompson

That's why I said "not using a plain positional notation". You can
represent the algebraic numbers, and all computable numbers, using
finite sequences of bits.

For example, you could represent each algebraic number as a list of
the integer coefficients of a polynomial it satifisfies, along with an
integer indicating which root it is. You could then encode the
sequence of integers as a sequence of bits in whatever manner you
prefer. Of course, the arithmetic operations are non-trivial given
this representation.

You can even represent a number by a character string specifying, in
unambiguous English, the computation of the number.

For example:

"e"
"the square root of pi"
"the smallest number not specifiable by a 60-character string"

(oops)
 
J

Joe Wright

Ian said:
Jordi said:
I have made a little C program that generates the following output:
227.000000 / 5 = 45.400002

Here's the code:
int main (int argc, char* argv[]) {
float var = 227;
printf("%f / 5 = %f\n", var, var / 5);
return 0;
}

Obviously, the output should be 45.4 (or 45.400000), but for some
mysterious reason C decided to add 0.000002* to the result. The error
might seem minor and this program is obviously trivial, but I need to
do similar calculations in a much bigger project and the problem I'm
encountering is caused by exactly this error.

* It seems that it is not .000002 is added, but rather
..0000015258789076710855... (which I found out after subtracting 45.4
from the result and then multiplying it with 10,000,000,000,000,000)

Why does C do this and what can I do about it?
It doesn't, the floating point hardware or emulation does.

Floating point isn't precise. Avoid floats and use doubles, even then,
you have to understand the limitations of floating point math.
Floating point is precise. It is not exact. C's float typically has 24
bits of precision and the value of a float can be precise to 1 in 16
million or so. The mantissa of the double is 53 bits and provides an
exquisitely precise (if not exact) value on the order of 9e15.
You must have a dodgy FPU, the result is 45.400000 on my system :)
There are no 'dodgy' FPU's among our audience.

I hope this doesn't wrap or that otherwise you can figure it out ..

01000010 00110101 10011001 10011010
Exp = 132 (6)
00000110
Man = .10110101 10011001 10011010
4.54000015e+01

This (above) is the result of 'float f = 227.0 / 5;'
The last line is the result of 'printf("%.8e", f);'

01000000 01000110 10110011 00110011 00110011 00110011 00110011 00110011
Exp = 1028 (6)
000 00000110
Man = .10110 10110011 00110011 00110011 00110011 00110011 00110011
4.5399999999999999e+01

This (above) is the result of 'double d = 227.0 / 5;'
The last line is the result of 'printf("%.16e", d);'
 
R

Richard Tobin

Keith Thompson said:
"the smallest number not specifiable by a 60-character string"

(oops)

Sorry, that would be defined to invoke undefined behaviour, and is
therefore off-topic.

-- Richard
 
J

Joe Wright

Jordi said:
I said I wouldn't try doubles in the real project, but it didn't prove
that difficult and I couldn't let it rest. Anyway, it didn't work as
well as I hoped. This is some of the output I got:
x = 64.200000 / (107.000000 / 5) = 64.200000 / 21.400000 = 3.000000 = 2

Notice the 3.000000 = 2? That's what's screwing my program over.

Anyway, as I understand from you guys, I need infinite bits to get 100%
accuracy. That's probably not going to happen anytime soon, so I need
another solution. Is there a way to make the program use the calculated
3.000000 instead of the 2.9999999999999999999999999 it actually
calculated? Is there some way to specify how accurate I need things?

Thanks for any help!



Ian said:
Jordi said:
I have made a little C program that generates the following output:
227.000000 / 5 = 45.400002

Here's the code:
int main (int argc, char* argv[]) {
float var = 227;
printf("%f / 5 = %f\n", var, var / 5);
return 0;
}

Obviously, the output should be 45.4 (or 45.400000), but for some
mysterious reason C decided to add 0.000002* to the result. The error
might seem minor and this program is obviously trivial, but I need to
do similar calculations in a much bigger project and the problem I'm
encountering is caused by exactly this error.

* It seems that it is not .000002 is added, but rather
..0000015258789076710855... (which I found out after subtracting 45.4
from the result and then multiplying it with 10,000,000,000,000,000)

Why does C do this and what can I do about it?
It doesn't, the floating point hardware or emulation does.

Floating point isn't precise. Avoid floats and use doubles, even then,
you have to understand the limitations of floating point math.

You must have a dodgy FPU, the result is 45.400000 on my system :)
We all need to appreciate that our computer works in binary, not
decimal. The result that you see from printf is a conversion from binary
floating point to decimal. The conversion is as good as anybody can do
but we have to know that while simple integrals (up to a point) can be
exactly represented in a binary floating point object, but as soon as we
get to the right of the radix point, it falls apart.

But, the floating point is the better number. If you don't get what you
like from printf, adjust the format parameters.
 
M

Malcolm

Flash Gordon said:
In fact, it is provable that the set of reals between 0 and 1 is larger
than the set of all integers.
Almost trival in fact.

int main(void)
{
if(sizeof(double) > sizeof(long))
printf("More reals\n");
else
printf("More integers\n");

return 0;
}
 
K

Keith Thompson

Malcolm said:
Almost trival in fact.

int main(void)
{
if(sizeof(double) > sizeof(long))
printf("More reals\n");
else
printf("More integers\n");

return 0;
}

If this were at all relevant to the original statement, I'd say
something about padding bits.
 
F

Flash Gordon

Malcolm said:
Almost trival in fact.

int main(void)
{
if(sizeof(double) > sizeof(long))
printf("More reals\n");
else
printf("More integers\n");

return 0;
}

That obviously does not prove the statement. sizeof(double) does not
tell you how many reals there are between 0 and 1.
 
K

Kevin Handy

inmatarian said:
Eww, top posting.

use used and (int) cast. At any rate, this isn't C's fault, it's the
fault of IEEE's float specification, that says that you only need 32
bits of precision, instead of infinity.

You must have a better set of memory chips than anyone else has.
Please post pictures of your memory box that holds these infinite
precision floating pointy numbers. Also, since you obviously have
the precision necessary, please tell us what the last three digits
of PI and e are. Always wondered if PI was a palindrome, or e with
the digits reversed.
 

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

Forum statistics

Threads
473,888
Messages
2,569,965
Members
46,294
Latest member
HollieYork

Latest Threads

Top