Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Rounding With sprintf anomaly?

by oakbox (Chaplain)
on Feb 19, 2002 at 09:01 UTC ( [id://146354]=perlquestion: print w/replies, xml ) Need Help??

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

I'm a little confused. Okay, I know that int does not round a number, it just lops off everything after the decimal. So, after reading some other posts here, I go with sprintf. Specifically, I need an integer and I want to correctly lop everything off after the decimal. Here's my formula:

$rounded=sprintf("%.0f",$unrounded); This works for most cases, but I'm getting some funny output from my script when the decimal is .5. Here is the verbose output from my script where it performs a calculation, tells me the unrounded number, and then tells me what the rounded number is:

(script output)
SO the equation to figure out sub trait A1 looks like this
7 + (2 - (0.5 * 9)) and comes up as 4.5
that is rounded to 4
SO the equation to figure out sub trait A2 looks like this
7 + (2 - (0.5 * 9)) and comes up as 4.5
that is rounded to 4
SO the equation to figure out sub trait A3 looks like this
7 + (5 - (0.5 * 9)) and comes up as 7.5
that is rounded to 8

So, my question is, why did it round DOWN for two cases, and round UP for one case?


-oakbox

Replies are listed 'Best First'.
Re: Rounding With sprintf anomaly?
by dash2 (Hermit) on Feb 19, 2002 at 12:28 UTC

    It's not an anomaly.

    Perl rounds scientifically. This is not the same way of rounding as accountants use. Accountants always round .5 up. That's probably what you were taught at school. It's predictable and good.

    Scientists don't always round .5 up. .5 is exactly half way between two numbers. If they always rounded up, you would get statistical bias in large collections of rounded data. .5 is rounded sometimes up and sometimes down. (I think the rule is something like even numbers (2.5, 4.5, 6.5 etc.) round down while odd numbers (1.5, 3.5 etc.) round up.) This method is scientifically correct, and good.

    Of course, there are also problems with INTs and FLOATs. For accounting correctness, one thing you can do is cheat:

    # untested if ($unrounded =~ /\.(\d)/ and $1 > 4) {$rounded = int ($unrounded + 1 +)} else {$rounded = int $unrounded}
    dave hj~
      In base 10, or any base equal to 2 modulo 4, round to even is preferred.
      In a base equal to 0 modulo 4, round to odd would be preferred.
Re: Rounding With sprintf anomaly?
by guha (Priest) on Feb 19, 2002 at 09:28 UTC
    What HW/OS/Perl version are you on ? On my Activestate 631 both variations in your example is rounded up.

    What I'm thinking of is that a floating point value not always kan be represented exactly and that your first and second case might be represented as 4.499999999999999..... in your box, which would explain the rounddown.

    You could look into the Posix routines for a possibly alternate route to achieve your aim.
    ---
    I would like to change the world but God won't let me have the source code.

      Intel chip, Linux (cobalt's hacked Redhat version), and Perl 5.6.1. POSIX 'floor' and 'ceiling' are mentioned in a couple of posts elsewhere on perlmonks. I'll look into those.

      I think the suggestion that the 5 isn't really a '5' holds the most water. There are actually about 28 separate calculations being performed in that section and it is only in THAT PARTICULAR section that the round doesn't perform as expected.

      Okay, I'm happy again. I just wanted an explanation for the (apparently) freaky behaviour.

      -oakbox

        I think if you want a proper rounding, you do this:

        $x=int($num+0.5);

        This gets rid of all those oddities, like a number looking like 5.5, but actually being 4.9999999....

        Sorry, typo, should be 5.499999...

(crazyinsomniac) Re: Rounding With sprintf anomaly?
by crazyinsomniac (Prior) on Feb 19, 2002 at 09:42 UTC
    I dunno what to tell you, except you're dealing with floats... deal with ints instead ... (I have no insight, only code, well a little insight - perl has internal ways of distinguishing numbers from strings .... (s)printf are c functions, do stuff similar to (un)pack in fsking with the numbers ... that kinda it, vague ain't it )
    #!/usr/bin/perl my $one = 7 + (2 - (0.5 * 9)); my $two = 7 + (2 - (0.5 * 9)); my $thr = 7 + (5 - (0.5 * 9)); for($one,$two,$thr) { printf "no_ROUND(%s) yes_ROUND(%d) foy_FLOAT(%f) foy_ROUND(%.0f)\n\n", $_,$_,$_,$_; =head2 UPDATE: uncomment this to add to the mistery print "pack i ".unpack('i*', pack('i*', $_))."\n"; print "pack l ".unpack('l*', pack('l*', $_))."\n"; print "pack f ".unpack('f*', pack('f*', $_))."\n"; print "pack d ".unpack('d*', pack('d*', $_))."\n"; =cut and don't forget cut# } __END__ =pod C:\>perl round.txt no_ROUND(4.5) yes_ROUND(4) foy_FLOAT(4.500000) foy_ROUND(5) no_ROUND(4.5) yes_ROUND(4) foy_FLOAT(4.500000) foy_ROUND(5) no_ROUND(7.5) yes_ROUND(7) foy_FLOAT(7.500000) foy_ROUND(8) =head1 that is an interesting way to get an int %% a percent sign %c a character with the given number %s a string %d a signed integer, in decimal %u an unsigned integer, in decimal %o an unsigned integer, in octal %x an unsigned integer, in hexadecimal %e a floating-point number, in scientific notation %f a floating-point number, in fixed decimal notation %g a floating-point number, in %e or %f notation =cut
    update: guha brings up a good point, ints/floats will still vary from system 2 system

     
    ______crazyinsomniac_____________________________
    Of all the things I've lost, I miss my mind the most.
    perl -e "$q=$_;map({chr unpack qq;H*;,$_}split(q;;,q*H*));print;$q/$q;"

Re: Rounding With sprintf anomaly?
by jujubee (Acolyte) on Feb 19, 2002 at 18:47 UTC
    the latest math::round's nearest function does check for big-endian or little-endian then does the right thing. Otherwise, a fix in another module uses 0.500000001 to do rounding. Of course this also exists in javascript where the simple solution is to use 0.500000001.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://146354]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (3)
As of 2024-04-25 17:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found