Я реализую пифагорейские средние в PHP, арифметические и геометрические средние — это кусок пирога, но мне очень трудно придумать надежныйгармоническое среднеереализация.
И это эквивалентная реализация в PHP:
function harmonicMeanV1()
{
$result = 0;
$arguments = func_get_args();
foreach ($arguments as $argument)
{
$result += 1 / $argument;
}
return func_num_args() / $result;
}
Теперь, если какой-либо из аргументов равен 0
, это вызовет предупреждение о делении на 0, но так как 1 / n
такое же, какn-1и pow(0, -1)
изящно возвращает константу INF
без каких-либо ошибок, я мог бы переписать это на следующий (, он все равно будет вызывать ошибки, если нет аргументы, но давайте проигнорируем их пока):
function harmonicMeanV2()
{
$arguments = func_get_args();
$arguments = array_map('pow', $arguments, array_fill(0, count($arguments), -1));
return count($arguments) / array_sum($arguments);
}
Обе реализации работают нормально в большинстве случаев (пример v1 , v2 и WolframAlpha), но они явно терпят неудачу Если сумма ряда1 / niравна 0 , я должен получить еще одно предупреждение о делении на 0, но я не...
Рассмотрим следующий набор:-2, 3, 6
(WolframAlpha говорит, что это сложный бесконечный):
1 / -2 // -0.5
+ 1 / 3 // 0.33333333333333333333333333333333
+ 1 / 6 // 0.16666666666666666666666666666667
= 0
Однако обе мои реализации возвращают-2.7755575615629E-17
как сумму(v1 , v2)вместо 0
.
Хотя результат возврата на CodePad: -108086391056890000
моя машина разработки (32 -бит )говорит, что это -1.0808639105689E+17
, тем не менее это совсем не похоже на 0
или INF
, которые я ожидал. Я даже пытался вызватьis_infinite()
для возвращаемого значения, но оно вернулось как false
, как и ожидалось.
Я также нашел функцию stats_harmonic_mean()
, которая является частью расширения stats
PECL, но, к моему удивлению, я получил точно такой же ошибочный результат:-1.0808639105689E+17
, если какой-либо из аргументов 0
, 0
равен возвращается, но не выполняется проверка суммы ряда,как вы можете видеть в строке 3585:
3557 /* {{{ proto float stats_harmonic_mean(array a)
3558 Returns the harmonic mean of an array of values */
3559 PHP_FUNCTION(stats_harmonic_mean)
3560 {
3561 zval *arr;
3562 double sum = 0.0;
3563 zval **entry;
3564 HashPosition pos;
3565 int elements_num;
3566
3567 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) {
3568 return;
3569 }
3570 if ((elements_num = zend_hash_num_elements(Z_ARRVAL_P(arr))) == 0) {
3571 php_error_docref(NULL TSRMLS_CC, E_WARNING, "The array has zero elements");
3572 RETURN_FALSE;
3573 }
3574
3575 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(arr), &pos);
3576 while (zend_hash_get_current_data_ex(Z_ARRVAL_P(arr), (void **)&entry, &pos) == SUCCESS) {
3577 convert_to_double_ex(entry);
3578 if (Z_DVAL_PP(entry) == 0) {
3579 RETURN_LONG(0);
3580 }
3581 sum += 1 / Z_DVAL_PP(entry);
3582 zend_hash_move_forward_ex(Z_ARRVAL_P(arr), &pos);
3583 }
3584
3585 RETURN_DOUBLE(elements_num / sum);
3586 }
3587 /* }}} */
Это похоже на типичную ошибку с плавающей точностью, но я не могу понять причину, почему, поскольку отдельные вычисления довольно точны:
Array
(
[0] => -0.5
[1] => 0.33333333333333
[2] => 0.16666666666667
)
Возможно ли работать обойти эту проблему, не возвращаясь к расширениям gmp
/bcmath
?