alarix has asked for the wisdom of the Perl Monks concerning the following question:

Honorable Monks,

I am VERY, VERY new to PERL. I've written approximately 10 scripts all together. I am also not the best at math, which you'll see soon.

Here's some background.
The latest (and by far the largest) script I've tried is an online form-based survey that stores the answers to multiple choice questions to a text file. I'm grabbing the file data with a script and outputting it to another HTML page which shows the number of answers per question-option, and the percentages.

For example, Question 1A had 4 answers. Question 1B had 8 answers. Question 1A has 33.33% and Question 1B has 66.66% of the total answers to Question 1.

Here's the problem.
I can NOT find a way to round off the decimals so that when I run a cross-tabulation, I can prove that 33.33 + 66.66 = 100. Many of the questions produce percentages such that the percentage is n.50000000000 (etc),which skews my expected total of 100. And this is turning my hairs grey!

Clarification: I can get everything to add up to 100 when I use decimals out to 5 digits. But NOT when I try to round off. (Powers that be do not want to look at ugly decimal points).

Here's the math, such as it is:

@N; #array to store denominator for each question for ($j=0; $j<$numPercents; $j++){ $thePercents[$k][$j] = (($theArrays[$k][$j] / $N[$j]) * 100) +; $thePercents[$k][$j] = sprintf "%3.5f", $thePercen +ts[$k][$j]; #I was trying to deal with the n.5 issue but this clearly won't work . +.. $factor = POSIX::floor($thePercents[$k][$j]); if (($thePercents[$k][$j] - .5) < $factor) { $thePercents[$k][$j] = POSIX::floor($th +ePercents[$k][$j]); } else { $thePercents[$k][$j] = POSIX::ceil($the +Percents[$k][$j]); } }

Please help!

Replies are listed 'Best First'.
Re: How to get rounded numbers to =100% ??
by artist (Parson) on May 22, 2003 at 03:29 UTC
    Hi alarix,

    If I understood your problem correctly, you might want to look at the Solution to Quiz by Dominus. He calls the procedure as fudge_numbers.

    So (4,8) can be converted to (33.33, 66.67) and you can display the 100%. Similarly (3, 3, 1) can be converted to (42.9, 42.9, 14.2) to get 100%.

    artist

      artist, That is exactly my problem! Thank you. Now I have to figure out how to make it work with my script. And what the expressions I don't understand mean! ;) Back to the books ...
Re: How to get rounded numbers to =100% ??
by rob_au (Abbot) on May 22, 2003 at 03:09 UTC
    Firstly, welcome to the Monastery alarix - And now on to your question ...

    There are *so* many ways to do this that you are probably going to be drowned in a sea of answers to this question - So rather than try and forward them all, this is how I would do it ...

    $thePercents[$k][$j] = int( $thePercents[$k][$j] + 0.5 );

    This approach uses the int function that returns the integer portion of a number. By incorporating the addition of the 0.5, this function can be used as a rudimentary rounding function for positive numbers - If there is a likelihood that you would be rounding negative numbers in this way, something like int( $number + 0.5 * ( $number < 0 ? -1 : 1 ) ) would be more appropriate.

     

    perl -le 'print+unpack("N",pack("B32","00000000000000000000001001011100"))'

Re: How to get rounded numbers to =100% ??
by Anonymous Monk on May 22, 2003 at 12:17 UTC
    Something else you may look into is separating your mantissa (display) from your value. you can keep the true number in one variable with high precision, then round the number off using sprintf.
    Real value        displayed
       _
    33.3               33.33 %
       _
    66.6               66.67 %
       _      _
    33.3 + 66.6        100.00 %
    

    The trick is to get perl to store the real values at the highest precision possible. I may not have expressed myself very well here, so someone else feel free to elaborate.

    hattmoward - mhoward%at%hattmoward.org
      I thought about this actually, but haven't got around to trying it out yet. But I think that's essentially what artist's Dominus reference is doing.

Re: How to get rounded numbers to =100% ??
by halley (Prior) on May 22, 2003 at 14:08 UTC

    Don't trust sprintf(). For whatever whim people had, it has some very arbitrary ideas about how to round numbers. On some platforms, it rounds 0.5 down, but 1.5 up. I did a test on Solaris and Windows, and the results varied. (Both versions just rely on their C runtime implementations of sprintf().)

    If you care about numbers, do the math yourself. Learn how floating point numbers on computers work, and where they're likely to introduce problems. For example, you're forcing a division before a multiplication in your loop. This reduces precision in floating point numbers. It's better to say $percent = ($x * 100 / $y);.

    Once you've got the floating value, use $rounded = int($unrounded + ($unrounded <=> 0)*0.5);. (This uses the spaceship operator like a sgn() function.)

    --
    [ e d @ h a l l e y . c c ]

      If you care about numbers, do the math yourself. Learn how floating point numbers on computers work, and where they're likely to introduce problems.

      The problem there being: to understand how floating point numbers on computers work, you have to understand how your particular architecture deals with floating point numbers -- as well as every other architecture your program might run on.

      (There are multiple IEEE floating point standards, not to mention that a lot of hardware doesn't do any of them right)

      I would suggest, in the spirit of your orriginal suggestion, that if you really care about precission, the best thing to do is to use an existing module designed to allow cross platform support for very precise mathematic operators (like Math::BigFloat) and if you really care about how the internals work, read the source.

      Floating point numbers ... I just did a quick search. perhaps something like this tutorial? (opens a new window) ... which, by the way, is the first time I've opened an applet that didn't crash my browser.