On Apr 26, 2010, at 11:22 AM, Uri Guttman wrote: >>>>>> "JWK" == John W Krahn <jwkr...@shaw.ca> writes: > > i must have not see the OP's reply yet so this is mostly for him. > >>> This is the routine as it looks now (I would be happy to hear about errors >>> or improvements that you might see) >>> sub round { >>> my $number = shift; >>> if ( $number == 0 ) { return 0.00; } > > that is no different than returning 0. > >>> if ( length( $number ) <= 4 ) { return $number; } >>> my $int = int( $number ); # Get the whole number >>> my $dec = $number - $int; # Get the decimals only >>> >>> $dec = sprintf( "%.15f", $dec ) * 1000000000000000; > > that will introduce more errors. you can just edit the text if you want > to mung the decimal point accurately. you are converting between numbers > and text back and forth for no reason. > >>> my @digits = split( //, $dec ); >>> my @reverse_digits = reverse( @digits ); >>> my $number_of_digits = $#reverse_digits - 1; > > this is insane code. > >>> my $remember = 0; >>> my $tracker = 0; >>> for( my $x = 0; $x <= $number_of_digits; $x++ ) { >>> $reverse_digits[$x] = $reverse_digits[$x] + $remember; >>> my $result = $reverse_digits[$x] + 5; >>> if ( $result >= 10 ) { >>> $remember = 1; >>> } else { >>> $remember = 0; >>> } >>> $tracker = $x; >>> } >>> if ( $reverse_digits[$tracker] >= 10 ) { >>> $reverse_digits[$tracker] = $reverse_digits[$tracker] - 10; >>> $reverse_digits[$tracker + 1]++; >>> if ( $reverse_digits[$tracker + 1] == 10 ) { >>> $reverse_digits[$tracker + 1] = 0; >>> $int++; >>> } >>> } >>> my @reordered_digits = reverse(@reverse_digits); >>> splice( @reordered_digits, 2 ); >>> my $decimal_amount = join( '', @reordered_digits ); >>> return "$int.$decimal_amount"; > > i can't see this code making any sense or doing the 'right' thing. you > need to learn how to let perl work for you. > > the classic way to round is to add .5 to the right decimal spot and then > truncate at the right spot. have you tried that? also perl does rounding > just fine. the problem is that you don't know what perl thinks your > numbers really are when you only print 2 decimal digits. print more (as > you do in that insane code) and you will see. finally as others have > stated, USE integers for money math. never use floats. then you will > never have this problem. > > uri > Or use COBOL or PL/I ;-). Seriously, though, you need to be using fixed-point arithmetic, which in Perl is only available as integer arithmetic. Doing it in cents is not enough--it has to be carried out to the tenth of a cent. To round, add 5 to the low-order digit, which represents a tenth of a cent, then round off the resulting low-order digit. You do need to keep track of the scale so you can put the decimal point in the right place when you write out the values. Even that method could result in some inaccurate rounding if the computer keeps integers in binary but you expect your rounding to be done in base 10.
I did a quick program a while back to compute stock basis. My proof results were a few cents off from those of the stock broker. I attribute that to the fact that I was using floating point and the "standard" rounding algorithm, instead of converting to fixed-point integer arithmetic. It didn't matter because I only needed the values for my tax returns, and a few cents has no negligible effect on the bottom line. Since you are using real accounting data, it really has to be accurate. For that, you need fixed-point currency calculations and a way to round in base 10. --Marilyn -- To unsubscribe, e-mail: beginners-unsubscr...@perl.org For additional commands, e-mail: beginners-h...@perl.org http://learn.perl.org/