Quickly reducing the number of decimal digits for a set of real numbers

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.

-
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 6:13
@Mr.Wizard I am using version 9.0.1.0 on a 64-bit operating system. – RM1618 Sep 19 '13 at 6: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 6: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 6:40
@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 6:50

2 Answers

Packing

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

DeveloperPackedArrayQ[testvalues]

False

packedvalues = DeveloperToPackedArray@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.

-
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 6:51
Data Shape: I get {0.876050, Null}. – RM1618 Sep 19 '13 at 6:52
SetAccuracy: I get {2.585148, Null}, similar to your numbers. – RM1618 Sep 19 '13 at 6:52
It looks like Packing solves my speed issue, but the lesson is that this only works in v9? – RM1618 Sep 19 '13 at 6:53
@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

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[(DeveloperToPackedArray@testvalues), .1]


(1.3 sec)

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

-
What testvalues` are you using? Surely not the 100x2 array with those timings. – Mr.Wizard Sep 21 '13 at 0:20