Hallo nochmal,
Ich habe einen kleinen Fehler in meiner JS-Version entdeckt: tmp_value wird in der eigentliche Rundungsfunktion nicht als Variable deklariert, hier nochmal die korrigierte Fassung:
(function () {
Math.ROUND_MODE_HALF_UP = 1;
Math.ROUND_MODE_HALF_DOWN = 2;
Math.ROUND_MODE_HALF_EVEN = 3;
Math.ROUND_MODE_HALF_ODD = 4;
var roundHelper = function (value, mode) {
var tmp_value;
if (value >= 0.0) {
tmp_value = Math.floor(value + 0.5);
if ((mode == Math.ROUND_MODE_HALF_DOWN && value == (-0.5 + tmp_value)) ||
(mode == Math.ROUND_MODE_HALF_EVEN && value == (0.5 + 2 * Math.floor(tmp_value/2.0))) ||
(mode == Math.ROUND_MODE_HALF_ODD && value == (0.5 + 2 * Math.floor(tmp_value/2.0) - 1.0)))
{
tmp_value -= 1.0;
}
} else {
tmp_value = Math.ceil(value - 0.5);
if ((mode == Math.ROUND_MODE_HALF_DOWN && value == (0.5 + tmp_value)) ||
(mode == Math.ROUND_MODE_HALF_EVEN && value == (-0.5 + 2 * Math.ceil(tmp_value/2.0))) ||
(mode == Math.ROUND_MODE_HALF_ODD && value == (-0.5 + 2 * Math.ceil(tmp_value/2.0) + 1.0)))
{
tmp_value += 1.0;
}
}
return tmp_value;
};
Math.roundToPlaces = function (value, places, mode) {
if (typeof(places) == 'undefined') places = 0;
if (typeof(mode) == 'undefined') mode = Math.ROUND_MODE_HALF_UP;
var f1, f2, tmp_value;
var precision_places = 14 - Math.floor(Math.log(Math.abs(value))/Math.LN10);
f1 = Math.pow(10.0, Math.abs(places));
if (precision_places > places && precision_places - places < 15) {
f2 = Math.pow(10.0, Math.abs(precision_places));
if (precision_places >= 0) {
tmp_value = value * f2;
} else {
tmp_value = value / f2;
}
tmp_value = roundHelper(tmp_value, mode);
f2 = Math.pow(10.0, Math.abs(places - precision_places));
// places < precision_places
tmp_value = tmp_value / f2;
} else {
if (places >= 0) {
tmp_value = value * f1;
} else {
tmp_value = value / f1;
}
// rounding not necessary
if (Math.abs(tmp_value) >= 1e15) {
return value;
}
}
tmp_value = roundHelper(tmp_value, mode);
if (Math.abs(places) < 23) {
if (places > 0) {
tmp_value = tmp_value / f1;
} else {
tmp_value = tmp_value * f1;
}
} else {
var str = "" + tmp_value + "e" + (-places);
tmp_value = parseFloat(str);
if (!isFinite(tmp_value)) {
tmp_value = value;
}
}
return tmp_value;
};
})();
Ich habe auf Ingos Wunsch das ganze auch nach PHP portiert:
define ('ROUND_MODE_HALF_UP', 1);
define ('ROUND_MODE_HALF_DOWN', 2);
define ('ROUND_MODE_HALF_EVEN', 3);
define ('ROUND_MODE_HALF_ODD', 4);
function _round_helper($value, $mode) {
if ($value >= 0.0) {
$tmp_value = floor($value + 0.5);
if (($mode == ROUND_MODE_HALF_DOWN && $value == (-0.5 + $tmp_value)) ||
($mode == ROUND_MODE_HALF_EVEN && $value == (0.5 + 2 * floor($tmp_value / 2.0))) ||
($mode == ROUND_MODE_HALF_ODD && $value == (0.5 + 2 * floor($tmp_value / 2.0) - 1.0)))
{
$tmp_value -= 1.0;
}
} else {
$tmp_value = ceil($value - 0.5);
if (($mode == ROUND_MODE_HALF_DOWN && $value == (0.5 + $tmp_value)) ||
($mode == ROUND_MODE_HALF_EVEN && $value == (-0.5 + 2 * ceil($tmp_value / 2.0))) ||
($mode == ROUND_MODE_HALF_ODD && $value == (-0.5 + 2 * ceil($tmp_value / 2.0) + 1.0)))
{
$tmp_value += 1.0;
}
}
return $tmp_value;
}
/**
* Round to a certain precision
*
* This method rounds to the nearest number of a certain precision in the
* specified rounding mode. Available modes are:
*
* ROUND_MODE_HALF_UP: Round .5 away from zero (arithmetic rounding)
* ROUND_MODE_HALF_DOWN: Round .5 away towards zero
* ROUND_MODE_HALF_EVEN: Round .5 to nearest even number (banker's rounding)
* ROUND_MODE_HALF_ODD: Round .5 to nearest odd number
*
* Please note that PHP does not have a platform-independent FP behaviour
* as of PHP 5.2. This will likely change in PHP 5.3. Until then, this method
* may still exhibit some strange results in very rare circumstances.
*
* This is a direct port of the C algorithm in
* <http://wiki.php.net/rfc/rounding>. Please note that this code is likely
* to be obsolete in PHP 5.3 as PHP's own round() function will likely share the
* same signature and implementation as this function.
*
* @param double $value The value to round
* @param int $places The number of places the result should have, may be
* negative for rounding to 10s or 100s etc.
* @param int $mode The rounding mode.
* @return double The rounded value
*/
function round_to_places($value, $places = 0, $mode = ROUND_MODE_HALF_UP) {
$places = (int)$places;
$value = (double)$value;
$precision_places = 14 - (int)floor(log10(abs($value)));
$f1 = pow(10.0, abs($places));
if ($precision_places > $places && $precision_places - $places < 15) {
$f2 = pow(10.0, abs($precision_places));
if ($precision_places >= 0) {
$tmp_value = $value * $f2;
} else {
$tmp_value = $value / $f2;
}
$tmp_value = _round_helper($tmp_value, $mode);
$f2 = pow(10.0, $precision_places - $places);
$tmp_value /= $f2;
} else {
if ($places >= 0) {
$tmp_value = $value * $f1;
} else {
$tmp_value = $value / $f1;
}
// rounding not necessary
if (abs($tmp_value) >= 1e15) {
return $value;
}
}
$tmp_value = _round_helper($tmp_value, $mode);
if (abs($places) < 23) {
if ($places > 0) {
$tmp_value = $tmp_value / $f1;
} else {
$tmp_value = $tmp_value * $f1;
}
} else {
$str = sprintf("%15fe%d", $tmp_value, -$places);
$tmp_value = (double)$str;
if (!is_finite($tmp_value) || is_nan($tmp_value)) {
return $value;
}
}
return $tmp_value;
}
Viele Grüße,
Christian