Compare, two identical numbers are not the same?!

J

Justin C

This one is really confusing me. I have a comparison:

$totalIndividualWeights != $param{weight}

and when they're both 42.6 (probably other numbers too, but this is the
problem someone has, and has come to me with) Perl says they're not the
same:

my $totalIndividualWeights = addIndividualWeights();

if ($totalIndividualWeights != $param{weight}){
print "Weight does not add up to total weight given on the first form.\$totalIndividualWeights = $totalIndividualWeights \n \$param{weight} = $param{weight}";
}

The print statement says both weights are 42.6. $param{weight} was
input by the user, $totalIndividualWeights is reached by adding several
user inputs: 2.86, 9.94, 9.98, 9.88, 9.94

Does perl have a problem with empty decimal places? What I mean is, the
inputs are to two decimals, but the result is only using one, is perl
thinking "42.60" and not matching 42.6?

Thank you for any help you can give with this.

Justin.
 
J

Jürgen Exner

Justin C said:
This one is really confusing me. I have a comparison:

$totalIndividualWeights != $param{weight}

and when they're both 42.6 (probably other numbers too, but this is the
problem someone has, and has come to me with) Perl says they're not the
same:

You forgot the first commandment of computer numerics:

Thou shalt not use equality for floating point numbers!

See 'perldoc -q 9999' for further details. Although those seem to be
unrelated questions/issues in fact they have the same underlying reason
which applies to virtually all programming languages.
The print statement says both weights are 42.6

Try printing them with 20 digits:
printf("%.20f", $totalIndividualWeights)
You may be surprised.

jue
 
J

Jürgen Exner

Glenn Jackman said:
I addition to all the better advice, did you chomp() your user input?
Are you comparing "42.6\n" to a number that's close to 42.6?

If that were the case then he should have gotten a warning about
non-numerical value used in numerical comparison. At least if he had
used warnings.

Technically it doesn't matter. He is using numerical compare "=", thus
Perl uses the arguments in numerical context and therefore automatically
converts the string "42.6\n" into the numerical floating point value
42.6 resp. something very close to it.

jue
 
J

Justin C

You forgot the first commandment of computer numerics:

Thou shalt not use equality for floating point numbers!

Being self-taught this had passed me by (I'm not qualified to teach
Perl you see, that's why my education was lacking this nugget - among
others).

OK. I can be certain that a user will never put in a number using more
than two decimal places - if they do I can reject it. Would I be better
off multiplying the input data by 100 and working in integars only? Then
dividing by 100 only when I need the "real" number? It sure looks like
it would make life easier.

On the other hand, the sprintf trick works. Maybe I should just try and
remember not to compare floats - that's obviously the best solution
because it'll save me asking again in the future.

Maybe, when I write my end of term report for myself, I should mention
this, maybe I'll remember it when I read what my teacher says about me.

See 'perldoc -q 9999' for further details. Although those seem to be
unrelated questions/issues in fact they have the same underlying reason
which applies to virtually all programming languages.


Try printing them with 20 digits:
printf("%.20f", $totalIndividualWeights)
You may be surprised.

Not *that* surprised. :) (at least, not after reading this thread!).


Justin.
 
C

ccc31807

OK. I can be certain that a user will never put in a number using more
than two decimal places - if they do I can reject it. Would I be better
off multiplying the input data by 100 and working in integars only?

Test that the absolute value of the difference between the two is less
than, say, 0.001, or whatever your tolerance is. You can create a user
defined function that returns true or false for your convenience, like

if (within_tolerance( val1, val2)) #if true
{ then_do_something(); }
else #if false
{ do_something_else(); }

CC
 
J

Jürgen Exner

Justin C said:
OK. I can be certain that a user will never put in a number using more
than two decimal places - if they do I can reject it. Would I be better
off multiplying the input data by 100 and working in integars only?

That would be one common approach to avoid any inaccuracies caused by
floating point arithmetic.
Then
dividing by 100 only when I need the "real" number? It sure looks like
it would make life easier.

Think of it as doing all calculations in cent instead of in dollar or
euro and only convert them into a 'human-friendly" format when printing
or reading those amounts.
If this works for your application then it's the best posible solution.

However, computer numerics are tricky, there is a reason why it is a
subject matter at university, and even that method is not fool-proof.

Example: when calculating interest on a long list of accounts, even if
you manage them internally in cent, you may get a different result when
adding up all the interest paid to each account compared to calculating
the sum of all accounts and then the interest on the lump sum. Those
rounding errors are what's driving accountants and bankers nuts because
it takes forever to find out where and why those 3 cents went missing in
action while the missing million will be spotted immediately.
On the other hand, the sprintf trick works. Maybe I should just try and
remember not to compare floats -

It is ok to compare floats for smaller/larger. But instead of checking
for equal you must check if the difference between the two numbers is
smaller than some tiny number. How large that tiny number is depends on
your application area, but it will never be 100% accurate.

jue
 
J

Justin C

That would be one common approach to avoid any inaccuracies caused by
floating point arithmetic.

I'll try and remember this for another time I might need it.

It is ok to compare floats for smaller/larger. But instead of checking
for equal you must check if the difference between the two numbers is
smaller than some tiny number. How large that tiny number is depends on
your application area, but it will never be 100% accurate.

Thank you, Jurgen and CC. I'm doing a straight compare of two sprintf
'numbers', but I quite like the idea of testing the difference - a
tollerance as CC put it. I might re-write the code for proof of concept,
and "doing it" actually gets it into my head better than reading it.

Justin.
 
S

sln

I'll try and remember this for another time I might need it.



Thank you, Jurgen and CC. I'm doing a straight compare of two sprintf
'numbers', but I quite like the idea of testing the difference - a
tollerance as CC put it. I might re-write the code for proof of concept,
and "doing it" actually gets it into my head better than reading it.

Justin.

The ballpark for comparisons are C functions ceil/floor (posix in perl).
Never can you compare doubles for equality, and never can you even see a
print of thier actual float value.

There is only one need for true comparisons, the is the case of
Numerical Methods absolute "difference" being less/greater than some
value. Or in the case of rounding is needed (ala the Pentium 60 bug
where zero is less than some absolute value of some really small fraction
of 1).

-sln
 

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,999
Messages
2,570,243
Members
46,836
Latest member
login dogas

Latest Threads

Top