Неточные числа с добавлением микротайма и с плавающей запятой в PHP - PullRequest
0 голосов
/ 23 июня 2009

У меня ужасное время, чтобы убедить себя в том, что я здесь сделал, это хорошая идея. Конкретный раздел, который я считаю нежелательным:

return ((float)($now+$sec).'.'.$mic);

Чтобы сохранить точность с плавающей запятой, я вынужден использовать библиотеки BC или GMP (ни одна из которых не всегда доступна). В этом случае я прибегал к глушению чисел вместе с конкатенацией строк.

<?php

// return the current time, with microseconds
function tick() {
    list($sec, $mic, $now) = sscanf(microtime(), "%d.%d %d");
    return ((float)($now+$sec).'.'.$mic);
}

// compare the two given times and return the difference
function elapsed($start, $end) {
    $diff = $end-$start;

    // the difference was negligible
    if($diff < 0.0001)
        return 0.0;

    return $diff;
}

// get our start time
$start = tick();

// sleep for 2 seconds (should be ever slightly more than '2' when measured)
sleep(2);

// get our end time
$end = tick();

$elapsed = elapsed($start, $end);

// should produce output similar to: float(2.00113797188)
var_dump($elapsed);

?>

Если я попытаюсь добавить два числа, например 123456789 (представляющих временную метку) и 0.0987654321 (представляющих микросекунды), используя оператор сложения ( + ) I неизменно в конечном итоге с 123456789.099. Даже при использовании целого числа с плавающей точкой результат остается тем же.

Есть ли решение для этой проблемы, которое 1) не является хаком и 2) не включает в себя конкатенацию строк? Мне не нужно прибегать к искаженному коду такого рода, чтобы получить точную временную метку с микросекундным разрешением.

Редактировать: Как объяснил С. Гериг, числа с плавающей запятой в PHP иногда могут быть немного сложными для отображения. «Точность», указанная в конфигурации PHP, относится к отображению. Фактические значения не округлены, как я думал. Гораздо более простое решение для приведенного выше кода будет выглядеть так:

// return the current time, with microseconds
function tick() {
    return microtime(true);
}

// compare the two given times and return the difference
function elapsed($start, $end) {
    return $end-$start;
}

// get our start time
$start = tick();

// sleep for 2 seconds (should be ever slightly more than '2' when measured)
sleep(2);

// get our end time
$end = tick();

$elapsed = elapsed($start, $end);

// should produce output similar to: float(2.00113797188)
var_dump($elapsed);

Если вы должны были проверить $ начало или $ конец , прежде чем вычесть одно из другого, может показаться, что они были округлены до сотых долей. Это не вариант. Кажется, что произвольная точность сохраняется для арифметики, в то время как отображение ограничено.

Ответы [ 3 ]

2 голосов
/ 23 июня 2009

Почему бы вам не использовать microtime(true), который просто возвращает метку времени микросекунды как float? Параметр [bool] $get_as_float был добавлен в PHP 5.0.0.

По поводу комментария о «потере» точности:

$start = microtime(true);
$end = microtime(true);
echo $end - $start;
// prints 7.1526861190796

microtime(true) не ограничивается двумя десятичными знаками. Плакат сталкивается с эффектом параметра конфигурации precision, который определяет, сколько десятичных знаков будет напечатано при выводе переменных с плавающей точкой. Это не имеет ничего общего с внутренней точностью, используемой microtime(true). Вы всегда можете использовать number_format() или (s)printf() для форматирования вывода с точностью тебе нравится.

1 голос
/ 11 сентября 2009

Во-первых, сплигак, я вижу, что ваш код содержит ошибку.

list($sec, $mic, $now) = sscanf(microtime(), "%d.%d %d");
return ((float)($now+$sec).'.'.$mic);

Если $ mic содержит менее шести цифр, вы получите результаты с мусором. Пройдите проверку на

the case where microtime() returns "0.000009 1234567890"

Во-вторых, вы можете значительно уменьшить ошибку с плавающей запятой следующим образом: (ВНИМАНИЕ: непроверенный код!)

// сравнить два заданных времени и вернуть разницу

// get our start time
$start = microtime();

// sleep for 2 seconds (should be ever slightly more than '2' when measured)
sleep(2);

// get our end time
$end = microtime();

// work around limited precision math
// subtract whole numbers from whole numbers and fractions from fractions

list($start_usec, $start_sec) = explode(" ", $start);
list($end_usec, $end_sec) = explode(" ", $end);
$elapsed = ((float)$end_usec)-((float)$start_usec);
$elapsed += ((float)$end_sec)-((float)$start_sec);

// please check the output
var_dump($elapsed);
0 голосов
/ 23 июня 2009

Типы с плавающей запятой изначально неточны. Либо живи с этим, либо не используй их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...