2017-02-21 49 views
0

Я наткнулся на странную проблему с intval() - мне нужно показать 2 числа после десятичной точки без округления результата. Поэтому я использую intval() таким образом:Intval() странное поведение - потеря точности

$result = ($data['a']/$data['b'])*100; 
$data['x'] = intval(($result)*100)/100; 

Таким образом, он отлично работает для почти всех результатов, но это показывает, какое-то странное поведение - вторая цифра после запятой идет вниз с 1 для некоторых результатов. Например:

$data['a'] = 16.58; (float) 
$data['b'] = 200; (int) 
$result = 8.29; 
$data['x'] = 8.28; 

($ данных [ «а»] всегда всплывают и [ «б»] всегда ИНТ)

Есть ли способ предотвратить этот странный эффект, и почему это происходит?

Заранее спасибо.

+0

Я думаю, что лучше было бы использовать 'bcdiv()': 'bcdiv (2,9999, 1, 2); // 2.99' [docs] (http://php.net/manual/en/function.bcdiv.php) – mineroot

+0

Вы пытались посмотреть bcmath lib? http://php.net/manual/en/book.bc.php –

+0

Или пол ($ result * 100)/100? Так как вам кажется, что вам нужна двузначная фиксированная ширина? – Fredster

ответ

0

Как насчет number_format вместо intval?

$var = number_format(12.3456, 2, '.', ''); // 12.34

EDIT

$var = floor(12.3456 * 100)/100; // 12.34 

EDIT 2

Это "взломать", но, по крайней мере, это работает.

$num = 8.294; 
$dotPos = strpos((string) $num, '.'); 
$num = substr((string) $num, 0, $dotPos + 3); // 3 and not 2 because index starts from 0 
$num = floatval($num); 

echo $num; // 8.29 
+1

Я только что подтвердил это: https://eval.in/740680 – C2486

+0

Видимо, также округляет последнюю цифру. Пример: number_format (1.5084586466165) = 1,51; –

+0

Вы правы. Я отредактировал свой ответ, но он не должен корректно работать – Grork

0

Хорошо, я вижу, что здесь происходит, но это довольно сложно объяснить.

Поплавки представлены внутренне как двоичное число с показателем экспоненты. Несмотря на то, что 8.29 является полностью точным числом в десятичной форме с показателем экспоненты, оно не находится в двоичном формате. Существует небольшое трение, поэтому на самом деле оно меньше, чем у 8.29.

Вы не видите его при печати, так как он округляется от магического поплавка PHP до строкового преобразования. Но если вы используете intval, это покажет.

Вы можете проверить здесь: http://www.exploringbinary.com/floating-point-converter/

8,29 десятичной = 8.28999996185302734375 в двоичной (или 1000.01001010001111010111)

UPDATE

Чтобы получить desrired результат в чистом виде:

$result = ($data['a']/$data['b'])*100; //8.28999996185302734375 internally or "8.29" as string 
$result = round($result * 100)/100; //rounded 828.999... rounded to 829 devided by 100 to 8.29 

ОБНОВЛЕНИЕ 2

Обратите внимание, что результат снова представлен как 8.28999996185302734375 из-за вышеупомянутых ограничений двоичных поплавков. Поэтому в этом специальном случае вся операция даже не нужна. Но в то же время он преобразует такие случаи, как 1.234 => 1.23 и 1.235 => 1.24, если они представлены в виде строк.

Если вы хотите, чтобы пол результат (1,235 => 1,23), но по-прежнему держать +8,28999996185302734375 => 8,29, то вам нужно

$result = ($data['a']/$data['b'])*100; 
$result = floor(round($result1 * 1000)/10)/100; 
+0

Извините, исправлена ​​ошибка. Это 16,58/200 = 0,0829 * 100 = 8,29. Это штрафное число, но поскольку все остальные цифры похожи на 29.04 ÷ 1825.00 = 0.015912329 * 100 = 1.591232877 Я тоже использую intval(). И вот тогда проблема появится в 8.28. –

+0

Знаете ли вы, как можно обойти эту проблему? –

+0

Ну, это очень зависит от вашей конечной цели. Если вы действительно ожидаете, что 8.29 здесь, вы можете преобразовать результат деления в реальную строку перед продолжением. '$ result = (string) ($ data ['a']/$ data ['b']) * 100;' – Torge

 Смежные вопросы

  • Нет связанных вопросов^_^