PHP ceil дава грешни резултати, ако входът е float без десетичен знак

Боря се с функцията ceil() на PHP, която ми дава леко грешни резултати - помислете за следното:

$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)

Което работи :)


person totallyNotLizards    schedule 19.10.2011    source източник


Отговори (6)


Това е повече от вероятно поради грешка с плаваща запетая.

Вместо това може да имате късмет да опитате тази процедура.

<?php
$num = 2.7*3;
echo rtrim(rtrim(sprintf('%.1f', $num), '0'), '.');
person erisco    schedule 19.10.2011
comment
това дава 2 знака след десетичната запетая, имам нужда от 1 - този код работи: echo rtrim(rtrim(sprintf('%.1f', $num), '0'), '.'); иначе, страхотен отговор, благодаря - person totallyNotLizards; 19.10.2011

Поплавъците могат да бъдат непостоянни. Не всички реални числа могат да бъдат правилно представени в краен брой двоични битове.
Както се оказва, десетична част от 0,7 е едно от тези числа (излиза 0,10 с безкрайност, повтаряща "1100" след нея). В крайна сметка получавате число, което е малко над 0,7, така че когато умножите по 10, имате цифра малко над 7.

Това, което можете да направите, е да направите проверка за здравина. Вземете вашата плаваща цифра и я извадете като цяло число. Ако получената стойност е по-малка от, да речем, 0,0001, считайте това за вътрешна грешка при закръгляване и я оставете такава, каквато е. Ако резултатът е по-голям от 0,0001, приложете ceil() нормално.

Редактиране: Забавен пример, който можете да направите, ако сте в Windows, за да покажете това, е да отворите вграденото приложение за калкулатор. Поставете „4“, след което приложете функция за квадратен корен (с x^y, където y=0,5). Ще видите, че правилно показва "2". Сега извадете 2 от него и ще видите, че нямате 0 като резултат. Това е причинено от вътрешни грешки при закръгляване, когато се опита да изчисли корен квадратен от 4. При показване на числото 2 по-рано, той знаеше, че тези много далечни завършващи цифри вероятно са грешка при закръгляване, но когато те са всичко, което е останало, получава малко объркан.

(Преди някой да ме хване за това, разбирам, че това е прекалено опростено, но въпреки това го смятам за достоен пример.)

person Mr. Llama    schedule 19.10.2011

Проблемът е, че числата с плаваща запетая РЯДКО са това, което очаквате да бъдат. Вашият 2.7*3 вероятно ще бъде нещо като 81.0000000000000000001, което ceil() е до 82. За такива неща ще трябва да обвиете вашите ceil/round/floor извиквания с някои проверки за точност, за да се справите тези допълнителни микроскопични разлики.

person Marc B    schedule 19.10.2011

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

echo rtrim(rtrim(sprintf('%f', $num), '0'), '.');
person თორნიკე ბაბუნაძე    schedule 22.05.2015

Защо не опитате това:

$num = 2.7*3; 
$num *= 100; 
$num = floor($num); 
$num /= 10; 
$num = ceil($num); 
$num /= 10;
person vinum    schedule 25.06.2015

Преобразувайте числото си в низ и залейте низа.

function roundUp($number, $decimalPlaces){
    $multi = pow(10, $decimalPlaces);
    $nrAsStr = ($number * $multi) . "";
    return ceil($nrAsStr) / $multi;
}
person Triggsy    schedule 10.02.2021