note 35098 deleted from function.round by pollita

From: Date: Tue, 19 Aug 2003 17:57:55 +0000
Subject: note 35098 deleted from function.round by pollita
References: 1  Groups: php.notes 
Request: Send a blank email to php-notes+get-54708@lists.php.net to get a copy of this message
Note Submitter: donnieb819 at hotmail dot NOSPAM dot com ---- I needed to be able to round a number based on its fractional rather than integer part, thus to the closest 1/2, or 1/3 and 2/3 or 1/4, 1/2 and 3/4, etc. I wrote a function to do it which may be of some use: // round num into an arbitrary number of sub-integer 'bins' according to the // standard rounding rules. Thus, for n = 2, there are two bins per integer, // values from x.0 to < x.5 and >= x.5 to (1 + x).0 // following this example, 1.1 with n = 2 rounds to 1.0, 1.2 to 1.0, 1.25 to 1.5, // 1.5 to 1.5, 1.50001 to 1.5, 1.75 to 2.0, 1.9 to 2.0 // The idea is that you can determine when to round by looking at the slope of // the tangent line on the abs sine wave modulated by sin(n * PI * x), where n // is the number of 'bins'. The sine wave can be thought of as a series of hills // where a real on either side of the hill will 'roll down' the hill, essentially // following the slope of its tangent, towards its nearest bin value. A point that // lands at the top of a hill (precisely in between two bin values) would // theoretically have a slope of 0 (doesn't seem to happen in practice, though // we can detect this situation) and needs a little 'push' in one direction or the // other. Push it forward (round up) for odds, or and backwards (round down) // for evens. function bin($num,$n = 2) { // easier to work only with positive values, then return the negative result if($num < 0) { $neg_f = true; // remember that the answer must be negative $num = abs($num); // use the absolute value } $num = rtrim($num,'0'); // get rid of trailing 0's $bin_val = 1 / $n; // the value which separates the n number of bins. $frac = $num - (int)($num); // the fractional piece of arg $int = (int)($num); // the integral piece of arg // the slope of the tangent of the sine wave at num $result = cos($n * M_PI * $num); // pretend we have abs sine by looking at the second derivative and if // it's negative, flip result. (This makes sense also if you graph // both sin(PI * x) and -sin(PI * x), the hills separating the bin values // become apparant, and the negative hills (valleys) are just ignored. if((-sin($n * M_PI * $num) > 0) || ($result == -1)) // also, -1 is the same as 1 $result = -$result; // if num is either an integer, or lands directly on a bin, it is rounded already // (landed directly between two hills) if(!fmod($frac,$bin_val)) { $result = 1; // num is exactly half-way between two bins (on top of a hill), so round // down on evens and up on odds } elseif(!fmod($frac,$bin_val / 2)) { if(!($int % 2)) // even: round up $result = 3; // (these '3' and '-3' don't matter, just needed a pos and neg non-one value to tell which way to round) else $result = -3; // odd: round down } // now we know whether we are rounding, so it's straightforward to determine // by how much we are rounding. if($result == 1) // not rounding { $ret = $num; } elseif($result < 0) { // round up $ret = $int + (int)($frac / $bin_val) * $bin_val + $bin_val; } elseif($result > 0) { // round down $ret = $int + (int)($frac / $bin_val) * $bin_val; } // preserve the sign if($neg_f) $ret = -$ret; return $ret; }

« previous php.notes (#54708) next »