Benchmark time!
For addition:
Code:
package cz.xkiv;
import java.math.BigDecimal;
import java.text.DecimalFormat;
public class XTest {
public static final long UPTO = 500000000l;
public static void main(String[] args) {
int pass = 0;
for (pass = 1; pass <= 3; pass++) {
System.out.println("pass " + pass);
BigDecimal res = BigDecimal.ZERO;
BigDecimal addd = new BigDecimal("1.0000000001");
double dres = 0d;
double daddd = 1.0000000001d;
long ts_start = System.currentTimeMillis();
for (long i = 0; i < UPTO; i++) {
res = res.add(addd);
if (0 == i % 10000000) {
System.out.print(".");
}
}
long ts_end = System.currentTimeMillis();
System.out.println(res.toString() + " - " + (ts_end - ts_start));
ts_start = System.currentTimeMillis();
for (long i = 0; i < UPTO; i++) {
dres = dres + daddd;
if (0 == i % 10000000) {
System.out.print(".");
}
}
ts_end = System.currentTimeMillis();
DecimalFormat df = new DecimalFormat("#.###########");
System.out.println(df.format(dres) + " - " + (ts_end - ts_start));
long lres = 0;
long ladd = 10000000001l;
ts_start = System.currentTimeMillis();
for (long i = 0; i < UPTO; i++) {
lres = lres + ladd;
if (0 == i % 10000000) {
System.out.print(".");
}
}
ts_end = System.currentTimeMillis();
System.out.println(Long.toString(lres) + " - " + (ts_end - ts_start));
}
}
}
results: (three passes to rule out startup effects; numbers after "-" are times in milliseconds to finish)
Code:
pass 1
..................................................500000000.0500000000 - 41079
..................................................500000000,0001163 - 12671
..................................................5000000000500000000 - 12422
pass 2
..................................................500000000.0500000000 - 45891
..................................................500000000,0001163 - 12562
..................................................5000000000500000000 - 13938
pass 3
..................................................500000000.0500000000 - 45625
..................................................500000000,0001163 - 12171
..................................................5000000000500000000 - 15016
Addition with BigDecimal is roughly 3-4 times slower than doubles. This is even better than I expected, and much better than what some people would have me believe.
Note how fixed-point (with 64-bit longs) is slightly slower than floating point, *and* has better precision (because it has 64 bits for the entire signed integer, while double has to devote some bits to the exponent).
Same thing with multiplication (longs now need scaling with the fixed-point base, so one extra division per multiplication), results only:
starting with zero, so the result should be zero:
Code:
pass 1
..................................................0E-2147483647 - 59531
..................................................0 - 14063
..................................................0 - 28297
pass 2
..................................................0E-2147483647 - 65203
..................................................0 - 13235
..................................................0 - 29546
pass 3
..................................................0E-2147483647 - 58266
..................................................0 - 13063
..................................................0 - 27359
BigDecimal is slightly slower here (roughly 5 times slower than double, say), and slightly confused about the exponent.
starting with one, so the multiplication actually does something:
...
well, now we are talking slow. I had to reduce the iterations significantly. 10000 times. Apparently default java implementation is M*N where M and N are numbers of significant digits in the multiplicands, which is ... very suboptimal algorithm?
Changing the multiplicand from 1.0000000001 to 1.1 ... first few "." are much faster then the last ones.
Doing only one pass, because the long tails at the end are long. Each multiplication adds 1 decimal place to the result, so we are now talking numbers with 50k decimal places. That means I am seeing a lot of performance lost to garbage collection too, and possibly swapping because I am doing this on a computer that is already memory starved before running another JVM.
Only doing 1 pass too, otherwise my console gets garbled
Code:
pass 1
..................................................[insert very long number here, I don't want to spam the server] - 1890
..................................................∞ - 0
..................................................-921991187 - 0
Now we are talking infinitely slow
Next try, the same but rescaling the BigDecimals to 10 digits after every multiplication (10 is what I am using for the fixed-point longs, except that BigDecimals will handle that properly even with numbers much larger than 1), with 5k iterations:
(edit: this is wrong results, I rounded every number to 10 digits, but then threw away te rounded number and used the non-rounded ...)
Code:
pass 1
.................................................. [insert very long number here] - 10859
.................................................. [insert another long number here, which is the same in the first 12 digits, different in 4 more digits, zeroed out in many many digits after that, and doesn't even *have* a decimal part] - 0
..................................................922152497 [completely wrong, but that's expected] - 0
Redoing this with actually using the rounded number:
Code:
pass 1
..................................................919233389885635058382647497107173754394058285838127213229214989249537223192967335883159291350381340682876744796509280825390008593162442569432185021507101782064811260496984702470941910972291462538984926091900.8425702751 - 47
..................................................919233389907432500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 - 0
..................................................922152497 - 0
And 500k iterations:
Code:
pass 1
.................................................. [looong number again] - 36343
..................................................∞ - 15
..................................................-922149003 - 31
Let's do less iterations of the multiplication (shorter numbers, more comparable computations), but repeated more times (to get the floating/fixed performance significantly above 0 ms). 50 multiplications, repeated 1M times.
Code:
pass 1
..................................................117.3908528769 - 13516
..................................................117,3908528797 - 1469
..................................................0000914651276 - 3422
There.
(aaand, I was doing the setscale wrong, and have to redo the last two, boooo!)
that's only 10 times slower at slightly better precision (tries to maintain decimal precision in all 10 decimal places, where doubles can't do that in the last few).
So ... three orders of magnitude? Not a chance.
You get "three orders of magnitude" "worse" performance when you are doing something that doubles can't do at all. Like saying that a flight around the world is slower than a walk to your refrigerator, or something silly like that.