2
$\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$
8
  • $\begingroup$ Floor[x+0.5]? $\endgroup$ Commented Mar 17, 2019 at 11:58
  • $\begingroup$ @Szabolcs But Floor gives integer. Round can round at any digit $\endgroup$ Commented Mar 17, 2019 at 12:02
  • 2
    $\begingroup$ Are your aware of RoundingRule? $\endgroup$ Commented Mar 17, 2019 at 12:25
  • 1
    $\begingroup$ Could extend the method proposed by @Szabolcs: myRound[x_, d_] := d*Floor[x/d + 1/2] $\endgroup$ Commented Mar 17, 2019 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$ Commented Mar 17, 2019 at 18:36

1 Answer 1

4
$\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$
15
  • $\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$ Commented Mar 17, 2019 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$ Commented Mar 17, 2019 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$ Commented Mar 17, 2019 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$ Commented Mar 17, 2019 at 18:38
  • 1
    $\begingroup$ @matheorem That's what happens (automatically) when you use real (floating-point) numbers on all common CPUs. $\endgroup$ Commented Mar 18, 2019 at 2:52

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.