PHP Ceil дает неправильные результаты, если входные данные с плавающей запятой без десятичной дроби - PullRequest
1 голос
/ 19 октября 2011

Я боролся с PHP-функцией ceil (), которая давала мне немного неправильные результаты - рассмотрим следующее:

$num = 2.7*3; //float(8.1)
$num*=10; //float(81)
$num = ceil($num); //82, but shouldn't this be 81??
$num/=10; //float(8.2)

У меня есть число, которое может иметь любое количество десятичных знаков, и мне нужно, чтобы оно было округлено до одного знака после запятой. то есть 8.1 должно быть 8.1, 8.154 должно быть 8.2, а 8 следует оставить как 8.

Как я понял, взять число, умножить на 10, ceil (), затем разделить на десять, но, как вы можете видеть, я получаю дополнительный .1 добавленный в некоторых обстоятельствах.

Может кто-нибудь сказать, почему это происходит и как это исправить?

Любая помощь с благодарностью

РЕДАКТИРОВАТЬ: имел + = 10 вместо * = 10: S

РЕДАКТИРОВАТЬ 2: Я не упомянул об этом явно, но мне нужно, чтобы десятичное число ВСЕГДА округлялось вверх, а не вниз - этот ответ наиболее близок к настоящему моменту:

rtrim(rtrim(sprintf('%.1f', $num), '0'), '.');

Однако округляет 3,84 до 3,8, когда мне нужно 3,9. Извините, это не было яснее: (

Окончательное редактирование:

Я закончил тем, что сделал:

$num = 2.7*3; //float(8.1)
$num*=10; //float(81)
$num = ceil(round($num, 2)); //81 :)
$num/=10; //float(8.1)

Что работает:)

Ответы [ 5 ]

2 голосов
/ 19 октября 2011

Это более чем вероятно из-за ошибки с плавающей запятой.

Возможно, вам повезет, попробовав эту процедуру.

<?php
$num = 2.7*3;
echo rtrim(rtrim(sprintf('%.1f', $num), '0'), '.');
2 голосов
/ 19 октября 2011

Поплавки могут быть переменчивыми. Не все действительные числа могут быть правильно представлены в конечном количестве двоичных разрядов.
Как оказалось, десятичное сечение 0,7 является одним из этих чисел (выходит 0,10 с бесконечностью, повторяющейся «1100» после него). В результате вы получите число, которое чуть выше 0,7, поэтому при умножении на 10 у вас будет цифра чуть выше 7.

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

Редактировать: Забавный пример, который вы можете сделать, если у вас есть окно, чтобы показать это, чтобы открыть встроенное приложение калькулятора. Положите «4», затем примените функцию квадратного корня (с x ^ y, где y = 0.5). Вы увидите, что правильно отображается «2». Теперь вычтите 2 из него, и вы увидите, что у вас нет 0 в результате. Это вызвано внутренними ошибками округления, когда он пытался вычислить квадратный корень из 4. При отображении числа 2 ранее он знал, что эти очень отдаленные конечные цифры были, вероятно, ошибкой округления, но когда все это осталось, он получает немного запутался.

(Прежде, чем кто-то придет ко мне по этому поводу, я понимаю, что это упрощено, но тем не менее я считаю это достойным примером.)

0 голосов
/ 25 июня 2015

Почему бы не попробовать это:

$num = 2.7*3; 
$num *= 100; 
$num = floor($num); 
$num /= 10; 
$num = ceil($num); 
$num /= 10;
0 голосов

Используйте %f вместо %.1f.

echo rtrim(rtrim(sprintf('%f', $num), '0'), '.');
0 голосов
/ 19 октября 2011

Проблема в том, что числа с плавающей запятой РЕДАКТОРНЫ, чем вы ожидаете.Ваш 2.7 * 3, вероятно, будет выглядеть как 81.0000000000000000001, то есть ceil () до 82. Для такого рода вещей вам придется обернуть ваши вызовы ceil / round / floor с некоторыми проверками точности, чтобы обработатьэти дополнительные микроскопические различия.

...