@@ -205,6 +205,14 @@ public static function grapheme_str_split(string $string, int $length)
205205        return  $ chunks
206206    }
207207
208+     public  static  function  bcceil (string  $ numstring 
209+     {
210+         if  (!is_numeric ($ num
211+             throw  new  \ValueError ('bcceil(): Argument #1 ($num) is not well-formed ' );
212+         }
213+         return  self ::bcround ($ num0 , \RoundingMode::PositiveInfinity);
214+     }
215+ 
208216    public  static  function  bcdivmod (string  $ num1string  $ num2int  $ scalenull ): ?array 
209217    {
210218        if  (null  === $ quot\bcdiv ($ num1$ num20 )) {
@@ -214,4 +222,200 @@ public static function bcdivmod(string $num1, string $num2, ?int $scale = null):
214222
215223        return  [$ quot\bcmod ($ num1$ num2$ scale
216224    }
225+ 
226+     public  static  function  bcfloor (string  $ numstring 
227+     {
228+         if  (!is_numeric ($ num
229+             throw  new  \ValueError ('bcfloor(): Argument #1 ($num) is not well-formed ' );
230+         }
231+         return  self ::bcround ($ num0 , \RoundingMode::NegativeInfinity);
232+     }
233+ 
234+     /** 
235+      * @param \RoundingMode|\RoundingMode::* $mode 
236+      */ 
237+     public  static  function  bcround (string  $ numint  $ precision0 , $ modestring 
238+     {
239+         if  (!is_numeric ($ num
240+             throw  new  \ValueError ('bcround(): Argument #1 ($num) is not well-formed ' );
241+         }
242+ 
243+         $ sign1 ;
244+         if  (''  !== $ num'- '  === $ num0 ] || '+ '  === $ num0 ])) {
245+             if  ('- '  === $ num0 ]) {
246+                 $ sign1 ;
247+             }
248+ 
249+             $ numsubstr ($ num1 );
250+         }
251+ 
252+         if  (false  !== strpos ($ num'. ' )) {
253+             [$ intPart$ fracPartarray_pad (explode ('. ' , $ num2 ), 2 , '' );
254+         } else  {
255+             $ intPart$ num
256+             $ fracPart'' ;
257+         }
258+ 
259+         if  (''  === $ intPart
260+             $ intPart'0 ' ;
261+         }
262+ 
263+         $ intPartself ::trimLeadingZeros ($ intPart
264+         $ fracPartstring ) $ fracPart
265+ 
266+         if  ($ precision0 ) {
267+             $ fracLength\strlen ($ fracPart
268+ 
269+             if  ($ precision$ fracLength
270+                 $ scaledInt$ intPartstring ) substr ($ fracPart0 , $ precision
271+                 $ scaledFracstring ) substr ($ fracPart$ precision
272+             } else  {
273+                 $ scaledInt$ intPart$ fracPartstr_repeat ('0 ' , $ precision$ fracLength
274+                 $ scaledFrac'' ;
275+             }
276+         } else  {
277+             $ shift$ precision
278+             $ intLength\strlen ($ intPart
279+ 
280+             if  ($ shift$ intLength
281+                 $ splitPos$ intLength$ shift
282+                 $ scaledIntsubstr ($ intPart0 , $ splitPos
283+                 $ scaledInt''  === $ scaledInt'0 '  : $ scaledInt
284+                 $ scaledFracsubstr ($ intPart$ splitPos$ fracPart
285+             } else  {
286+                 $ scaledInt'0 ' ;
287+                 $ scaledFracstr_repeat ('0 ' , $ shift$ intLength$ intPart$ fracPart
288+             }
289+         }
290+ 
291+         $ roundedIntself ::roundIntegerPart ($ scaledInt$ scaledFrac$ sign$ mode
292+         $ isZero''  === trim ($ roundedInt'0 ' );
293+         $ absResultself ::formatRoundedDigits ($ roundedInt$ precision
294+ 
295+         if  (-1  === $ sign$ isZero
296+             $ absResult'- ' .$ absResult
297+         }
298+ 
299+         return  $ absResult
300+     }
301+ 
302+     private  static  function  roundIntegerPart (string  $ intPartstring  $ fracPartint  $ sign$ modestring 
303+     {
304+         $ intPartself ::trimLeadingZeros ($ intPart
305+ 
306+         if  (''  === $ fracPart''  === trim ($ fracPart'0 ' )) {
307+             return  $ intPart
308+         }
309+ 
310+         $ firstDigit$ fracPart0 ];
311+         $ tailstring ) substr ($ fracPart1 );
312+         $ tailNonZero''  !== trim ($ tail'0 ' );
313+         $ isGreaterThanHalf$ firstDigit'5 '  || ('5 '  === $ firstDigit$ tailNonZero
314+         $ isExactlyHalf'5 '  === $ firstDigit$ tailNonZero
315+         $ shouldIncreasefalse ;
316+ 
317+         switch  ($ mode
318+             case  \RoundingMode::TowardsZero:
319+                 break ;
320+ 
321+             case  \RoundingMode::AwayFromZero:
322+                 $ shouldIncreasetrue ;
323+                 break ;
324+ 
325+             case  \RoundingMode::PositiveInfinity:
326+                 $ shouldIncrease$ sign0 ;
327+                 break ;
328+ 
329+             case  \RoundingMode::NegativeInfinity:
330+                 $ shouldIncrease$ sign0 ;
331+                 break ;
332+ 
333+             case  \RoundingMode::HalfAwayFromZero:
334+                 $ shouldIncrease$ isGreaterThanHalf$ isExactlyHalf
335+                 break ;
336+ 
337+             case  \RoundingMode::HalfTowardsZero:
338+                 $ shouldIncrease$ isGreaterThanHalf
339+                 break ;
340+ 
341+             case  \RoundingMode::HalfEven:
342+                 if  ($ isGreaterThanHalf
343+                     $ shouldIncreasetrue ;
344+                 } elseif  ($ isExactlyHalfself ::lastDigit ($ intPart2  === 1 ) {
345+                     $ shouldIncreasetrue ;
346+                 }
347+                 break ;
348+ 
349+             case  \RoundingMode::HalfOdd:
350+                 if  ($ isGreaterThanHalf
351+                     $ shouldIncreasetrue ;
352+                 } elseif  ($ isExactlyHalfself ::lastDigit ($ intPart2  === 0 ) {
353+                     $ shouldIncreasetrue ;
354+                 }
355+                 break ;
356+         }
357+ 
358+         if  ($ shouldIncrease
359+             $ intPartself ::incrementDigits ($ intPart
360+         }
361+ 
362+         return  self ::trimLeadingZeros ($ intPart
363+     }
364+ 
365+     private  static  function  formatRoundedDigits (string  $ roundedIntint  $ precisionstring 
366+     {
367+         if  ($ precision0 ) {
368+             if  (\strlen ($ roundedInt$ precision
369+                 $ roundedIntstr_pad ($ roundedInt$ precision1 , '0 ' , STR_PAD_LEFT );
370+             }
371+ 
372+             $ intDigitssubstr ($ roundedInt0 , -$ precision
373+             $ fracDigitssubstr ($ roundedInt$ precision
374+ 
375+             $ intDigitsself ::trimLeadingZeros (''  === $ intDigits'0 '  : $ intDigits
376+             $ fracDigitsstr_pad ($ fracDigits$ precision'0 ' , STR_PAD_LEFT );
377+ 
378+             return  $ intDigits'. ' .$ fracDigits
379+         }
380+ 
381+         if  (0  === $ precision
382+             return  self ::trimLeadingZeros ($ roundedInt
383+         }
384+ 
385+         $ shift$ precision
386+         $ digits$ roundedIntstr_repeat ('0 ' , $ shift
387+ 
388+         return  self ::trimLeadingZeros ($ digits
389+     }
390+ 
391+     private  static  function  incrementDigits (string  $ digitsstring 
392+     {
393+         $ digits''  === $ digits'0 '  : $ digits
394+         $ index\strlen ($ digits1 ;
395+         $ result$ digits
396+         $ carry1 ;
397+ 
398+         while  ($ index0  && $ carry
399+             $ valueord ($ result$ index48  + $ carry
400+             $ carry$ value10  ? 1  : 0 ;
401+             $ result$ indexchr (48  + ($ value10 ));
402+             --$ index
403+         }
404+ 
405+         return  $ carry'1 ' .$ result$ result
406+     }
407+ 
408+     private  static  function  trimLeadingZeros (string  $ digitsstring 
409+     {
410+         $ digitsltrim ($ digits'0 ' );
411+ 
412+         return  ''  === $ digits'0 '  : $ digits
413+     }
414+ 
415+     private  static  function  lastDigit (string  $ digitsint 
416+     {
417+         $ length\strlen ($ digits
418+ 
419+         return  $ lengthord ($ digits$ length1 ]) - 48  : 0 ;
420+     }
217421}
0 commit comments