Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Padding with sprintf changing number

by sachin raj aryan (Acolyte)
on Sep 24, 2021 at 11:12 UTC ( #11136982=perlquestion: print w/replies, xml ) Need Help??

sachin raj aryan has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks Not getting why amount is changing after padding with sprintf.Can anyone tell why this kind of output this line print "$amount i m checking amount before padding,\n"; --> output is 488715 this line print "$padamnt i m checking amount after padding,\n"; ---> output is 0000000000488714

sub amnt($amn) { my $amount=$_[0]; print "$amount i m checking amount before padding,\n"; my $padamnt = sprintf("%016d",$amount); print "$padamnt i m checking amount after padding,\n"; return $padamnt; }

Replies are listed 'Best First'.
Re: Padding with sprintf changing number
by haukex (Bishop) on Sep 24, 2021 at 11:35 UTC

    The number stored in $amount is likely to be something like 488714.9999999999 instead of 488715, and since Perl is unlikely to simply upconvert an integer of that size to a floating-point number on its own, it's likely that you're doing some kind of floating-point operations on the number that you haven't shown us. See What Every Programmer Should Know About Floating-Point Arithmetic.

    The Perl Cookbook says "When Perl is told to print a floating-point number but not told the precision, it automatically rounds that number to however many decimal digits of precision that your machine supports." If you don't mind the performance hit, you could use bignum for precise calculations. If you don't mind the imprecision (which would not be advisable if this "amount" has anything to do with money), you could use the %016.0f as suggested by choroba. (Update: If all of the calculations you're doing deal only with integers, and in particular if you're ok with integer division, you could also consider integer. Update 2: The reply indicates that this is not an option in this case.)

      This was the operation i was doing it before padding

      print " Checking amnt before conversion $amt,\n"; $amt= sprintf("%.2f",$amt); print " Checking amnt after rounding $amt,\n"; $amt = $amt*100;

        4887.15 cannot be stored accurately as a floating-point number, which is may be is causing your issue. See my post above for more information.

      I am not sure how things changed please see my output 1348 4887.15 -----> this is the line i copy pasted no operation Checking amnt before conversion 4887.15, Checking amnt after rounding 4887.15, Checking amnt after conversion 488715, 488714 i m checking amount before padding, 0000000000488714 i m checking amount after padding, why it

      print " Checking amnt before conversion $amt,\n"; $amt= sprintf("%.2f",$amt); print " Checking amnt after rounding $amt,\n"; $amt = $amt*100; print " Checking amnt after conversion $amt,\n"; sub amnt($amn) { my $amount=$_[0]; $amount=int($amount); say "$amount i m checking amount before padding,\n"; #my $padamnt = sprintf("%016.0f",$amount);---> currently commented to +check integer effect. my $padamnt = sprintf("%016d",$amount); say "$padamnt i m checking amount after padding,\n"; return $padamnt; }
        I've rewritten your code into a form that I think makes at least some sense:
        use strict; use warnings; my $amt = 4887.15; print " Checking amnt before conversion $amt,\n"; $amt= sprintf("%.2f",$amt); print " Checking amnt after rounding $amt,\n"; $amt = $amt*100; print " Checking amnt after conversion $amt,\n"; printf "Checking the EXACT value of amnt: %.17g\n", $amt; amnt($amt); sub amnt { my $amount=$_[0]; $amount=int($amount); print "$amount i m checking amount before padding,\n"; #my $padamnt = sprintf("%016.0f",$amount);---> currently commented to +check integer effect. my $padamnt = sprintf("%016d",$amount); print "$padamnt i m checking amount after padding,\n"; return $padamnt; }
        When I run that script, I get:
        Checking amnt before conversion 4887.15, Checking amnt after rounding 4887.15, Checking amnt after conversion 488715, Checking the EXACT value of amnt: 488714.99999999994 488714 i m checking amount before padding, 0000000000488714 i m checking amount after padding,
        Note that I've added a line of code that demonstrates that the EXACT value of $amt is NOT 488715.
        Perl's print() function frequently fails to produce an accurate representation of floating point values - and that's what is happening here.

        That output I'm seeing is as I expect.
        Which is the line of output that you don't understand ?

        You need to stay alert to this aspect of perl's print() function.
        Neither python3 nor raku are afflicted with such a poorly designed implementation:
        $ python3 -c "print(4887.15 * 100)" 488714.99999999994 $ raku -e "say 4887.15e0 * 100" 488714.99999999994


        Cheers,
        Rob
Re: Padding with sprintf changing number
by choroba (Archbishop) on Sep 24, 2021 at 11:17 UTC
    I tried it with 1/3:
    #! /usr/bin/perl use warnings; use strict; use feature qw{ say }; sub amnt { my ($amount) = @_; say "$amount I'm checking amount before padding"; my $padamnt = sprintf("%016d",$amount); say "$padamnt I'm checking amount after padding"; } amnt(1/3);

    As expected, before padding its 0.333333333333333, after padding it's 0000000000000000. Rounding.

    Now try it with 1.999999999999999. print doesn't output the precise value.

    2 I'm checking amount before padding 0000000000000001 I'm checking amount after padding

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

      how to avoid this ..i dont want rounding after padding

        What about %016.0f?
        map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Padding with sprintf changing number
by BillKSmith (Monsignor) on Sep 24, 2021 at 11:36 UTC
Re: Padding with sprintf changing number
by Marshall (Canon) on Sep 24, 2021 at 21:36 UTC
    As another thought for you...consider the use of functions in the POSIX module to specify exactly what you want.
    Of interest: "ceil", "floor", "lround", "modf", "round", "trunc".

    Some of the these functions didn't appear until Perl 5.22. So the newer functions could be an issue if you have an old Perl.

    Also read the documentation carefully. Some of these functions return an actual integer. Some return a float with the fractional part set to zero.

    These functions should be pretty fast (at least they are in C). In some cases, it might be appropriate to call one of these functions to make it crystal clear to the reader your exact intent even though you might follow it with some kind of printf or sprintf.

Re: Padding with sprintf changing number
by hrcerq (Scribe) on Sep 25, 2021 at 18:30 UTC

    Hi.

    Not sure what you tried to do here, but there's a syntax mistake on the following line:

    sub amnt($amn)

    If you wanted to use a prototype, then it should be written like this:

    sub amnt($)

    return on_success() or die;

        If OP had activated this feature , then we would probably see the variable $amn appearing in the function's body.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (2)
As of 2021-10-17 07:41 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My first memorable Perl project was:







    Results (71 votes). Check out past polls.

    Notices?