5

How can I quickly convert a number with $n$ decimal points to a number of with $m$ decimal points? Round works, however, it is slower than I would like. This example rounds a set of $100$ real numbers to $0.1$ decimal precision:

testvalues = Table[{RandomReal[], RandomReal[]}, {i, 1, 100}]

t1 = AbsoluteTime[];

For[i = 1, i <= 10^5, i++,
  Round[testvalues, 0.1];
];

t2 = AbsoluteTime[];

t2 - t1

Takes $\approx 11.87$ seconds on my 3.47 GHz CPU. Floor and Ceiling take a commensurate amount of time.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
RM1618
  • 742
  • 4
  • 12
  • User, what version of Mathematica are you using? I am using version 7 and get timings that are within an order of yours. Another user reports timings that are an order of magnitude faster. – Mr.Wizard Sep 19 '13 at 06:13
  • @Mr.Wizard I am using version 9.0.1.0 on a 64-bit operating system. – RM1618 Sep 19 '13 at 06:16
  • User, please try the code in my answer and report your timings after packing; I believe you may get a much larger improvement than I did. – Mr.Wizard Sep 19 '13 at 06:32
  • Evaluating SeedRandom[42]; testvalues = RandomReal[1., {100, 2}]; For[i = 1, i <= 100000, i++, Round[testvalues, 0.1]]// AbsoluteTiming on my system gives {0.713849, Null} which is way faster than what the OP is seeing. I'm using V.9.0.1 on OS X 10.6.8 on a three year old iMac with a 2.93 MHz i7 iMac. I am very puzzled by the discrepancy. – m_goldberg Sep 19 '13 at 06:40
  • @m_goldberg By using RandomReal[1., {100, 2}] you are producing a packed array. I suspect that v9 (at least) has optimized Round for such cases yielding the better timing. – Mr.Wizard Sep 19 '13 at 06:48
  • 2
    @Mr.Wizard I ran your code for Packing: unpacked values gives {11.361650, Null} and packed values gives {0.640037, Null}. Hmmm.... – RM1618 Sep 19 '13 at 06:50
  • fyi, I experience about the same speed diff as @user9603 in the last comment (MacOSX 10.8.4, v9.01) – Pinguin Dirk Sep 19 '13 at 07:08

2 Answers2

4

Packing

You should make sure that your data is packed if at all possible:

Developer`PackedArrayQ[testvalues]
False
packedvalues = Developer`ToPackedArray@testvalues;

This at least speeds things a bit (timings in version 7 under Windows):

Do[Round[testvalues, 0.1], {10^5}]   // AbsoluteTiming
Do[Round[packedvalues, 0.1], {10^5}] // AbsoluteTiming
{7.0500098, Null}

{6.0500085, Null}

In version 9, and possibly 8, you should see a much greater improvement from packing that I experienced here in version 7. Other users are reporting well over an order of magnitude improvement in later versions.

Note that if you had generated the values with RandomReal[1, {100, 2}] they would have been packed to start with.

Data shape

In version 7, where the Round operation is handled by the Mathematica Kernel rather than the Intel MKL transposing the values before rounding makes a considerable difference:

Do[Round[packedvalues\[Transpose], 0.1]\[Transpose], {10^5}] // AbsoluteTiming
{3.8900054, Null}

SetAccuracy

Also applicable to version 7, an alternative that may be acceptable it is to use SetAccuracy which on my system this about twice as fast:

tvalues = packedvalues\[Transpose];

Do[SetAccuracy[tvalues, 2], {10^5}] // AbsoluteTiming
{1.7961027, Null}

Note that users of more recent versions will find that Round on a packed array is faster than SetAccuracy.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • I have {11.361650, Null} for unpacked values and {0.640037, Null} for packed values. Why am I seeing an order of magnitude speedup relative to you? – RM1618 Sep 19 '13 at 06:51
  • Data Shape: I get {0.876050, Null}. – RM1618 Sep 19 '13 at 06:52
  • SetAccuracy: I get {2.585148, Null}, similar to your numbers. – RM1618 Sep 19 '13 at 06:52
  • It looks like Packing solves my speed issue, but the lesson is that this only works in v9? – RM1618 Sep 19 '13 at 06:53
  • @user9603 Presumably Round (and probably Floor and Ceiling) was optimized for packed arrays at some time after version 7. I cannot say if the optimization was present in version 8. – Mr.Wizard Sep 19 '13 at 07:06
  • Hmm, kind of interesting. I wish there was more of a heads-up about these kind of things. – RM1618 Sep 19 '13 at 07:09
  • @user9603 You're not alone in that. Quite often new features are introduced without fanfare. A recent one that sticks in the mind is this. – Mr.Wizard Sep 19 '13 at 07:10
  • I don't understand why your timing with a packed array is so high. I get a value of about 0.6 sec. when I run your code. – m_goldberg Sep 19 '13 at 07:22
  • @m_goldberg I don't know what else there is to say: I'm using version 7 and in version 7 this operation is handled by the Kernel rather than being offloaded to the Intel MKL (where it is also parallelized). – Mr.Wizard Sep 19 '13 at 07:25
  • The reason I'm still flogging this horse is that your answer gives the reader the impression that the SetAccuracy approach is the fastest. But it seems clear that the packed array approach is almost an order of magnitude faster in V.9. Perhaps you might edit your answer to say something about that. – m_goldberg Sep 19 '13 at 07:39
  • To be fair don't you need to include the packing operation in the loop? – george2079 Sep 19 '13 at 15:04
  • 1
    @george2079 My point is that the numbers should have been packed on generation or at least before further operations, therefore no, I do not believe it should have been included in the loop. – Mr.Wizard Sep 19 '13 at 18:39
  • @m_goldberg Okay, I get it now. Please see my update and tell me if it satisfies. – Mr.Wizard Sep 19 '13 at 18:43
  • Yes, I am satisfied by the edit you made concerning SetAccuracy vs. Round on a packed array. – m_goldberg Sep 19 '13 at 21:10
0

For this example I get a 4-fold speed increase using Map:

 Round[testvalues, .1]

(* ~12 sec *)

 Map[Round[#, .1] &, testvalues, {2}]

(* ~3 sec *)

Edit, note if we do this with packedarrays the direct round applicaiton wins..

 Map[Round[#, .1] &, packedvalues, {2}]

(* 3 sec *)

Round[packedvalues, .1]

(* 0.9 sec *)

If you include the packing operation in the timing its still pretty good..

Round[(Developer`ToPackedArray@testvalues), .1]

(1.3 sec)

... All timing with the For loop in the original Question ...

george2079
  • 38,913
  • 1
  • 43
  • 110