1
$\begingroup$

As pointed out in this post, Mathematica has a special version of Round that

Round rounds numbers of the form x.5 toward the nearest even integer.

A comment by David G suggest that why not have differnt options Direction → {"HalfDown","HalfUp","HalfEven","HalfOdd","Stochastic"}

These days I need a version of Round to HalfUp. I write a quite ugly and slow function as below

myRound[x_, d_] := Module[{},
  c1 = (1./d)*10;
  c2 = 1./d;
  theDigit = Last@IntegerDigits@IntegerPart[x*c1];
  If[theDigit >= 5,
   Internal`StringToDouble@ToString@N[(IntegerPart[x*c2] + 1)/c2],
   Internal`StringToDouble@ToString@N[(IntegerPart[x*c2])/c2]]]

speed test

In[267]:= 
myRound[#, 0.01] & /@ RandomReal[1., 1000000]; // AbsoluteTiming

Out[267]= {30.7072, Null}

In[268]:= 
Round[#, 0.01] & /@ RandomReal[1., 1000000]; // AbsoluteTiming

Out[268]= {0.285921, Null}

So I am wondering if someone on this site already have developed an efficient toolkit for round matters?

$\endgroup$
  • $\begingroup$ Floor[x+0.5]? $\endgroup$ – Szabolcs Mar 17 at 11:58
  • $\begingroup$ @Szabolcs But Floor gives integer. Round can round at any digit $\endgroup$ – matheorem Mar 17 at 12:02
  • 2
    $\begingroup$ Are your aware of RoundingRule? $\endgroup$ – Silvia Mar 17 at 12:25
  • 1
    $\begingroup$ Could extend the method proposed by @Szabolcs: myRound[x_, d_] := d*Floor[x/d + 1/2] $\endgroup$ – Daniel Lichtblau Mar 17 at 13:58
  • 1
    $\begingroup$ That's a result of using decimal values e.g. .01 that do not have exact binary equivalents. Could instead do myRound[8.121,1/100] and numericize afterward. $\endgroup$ – Daniel Lichtblau Mar 17 at 18:36
3
$\begingroup$

I offer the following solution

r2[x_, a_] := x - Mod[x, a, -(a/2)]

We can verify that it has the desired result, using PiecewiseExpand

PiecewiseExpand[r2[x, a], -2 a < x < 2 a && a > 0]

Performance is only a little slower than the built-in Round

list = RandomReal[{0, 1}, 1000000];

AbsoluteTiming[Round[list, 0.1];]
(* {0.0079, Null} *)

AbsoluteTiming[r2[list, 0.1];]
(* {0.009414, Null} *)
$\endgroup$
  • $\begingroup$ Wow, great solution. But there is a issue with hidden fractions as pointed out by mathematica.stackexchange.com/q/65298/4742 . But if we use InternalStringToDouble@ToString`, the performance will drop down. $\endgroup$ – matheorem Mar 17 at 13:22
  • 4
    $\begingroup$ @matheorem Technically, 1.265 in binary floating point corresponds to the fraction 5697053528623677/4503599627370496 which less than 1265/1000. The issue is due to rounding decimal to binary. Thus it is a problem of inputting the number you meant, not a problem with r2[]. $\endgroup$ – Michael E2 Mar 17 at 15:14
  • 2
    $\begingroup$ @matheorem The problem with imprecise calculation is that unwanted errors creep in, such as what you're complaining about. :) How about this?: r2[x_, a_] := x (1 + Sign[x] $MachineEpsilon) - Mod[x (1 + Sign[x] $MachineEpsilon), a, -(a/2)] $\endgroup$ – Michael E2 Mar 17 at 15:40
  • 2
    $\begingroup$ @matheorem, I think you need to consider why you know the input is exactly 1.265 rather than a little more or a little less, and why the rounding is important to you. If you know the 3rd decimal place is exact, I suggest you multiply all your numbers by 1000, and work with integers. $\endgroup$ – mikado Mar 17 at 18:38
  • 1
    $\begingroup$ @matheorem That's what happens (automatically) when you use real (floating-point) numbers on all common CPUs. $\endgroup$ – Michael E2 Mar 18 at 2:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.