Background: I'm writing some geometry software in Java. I need the precision offered by Java's BigDecimal class. Since BigDecimal doesn't have support for trig functions, I thought I'd take a look at how Java implements the standard Math library methods and write my own version with BigDecimal support.
Reading this JavaDoc, I learned that Java uses algorithms "from the well-known network library netlib as the package "Freely Distributable Math Library," fdlibm. These algorithms, which are written in the C programming language, are then to be understood as executed with all floating-point operations following the rules of Java floating-point arithmetic."
My Question: I looked up fblibm's sin function, k_sin.c, and it looks like they use a Taylor series of order 13 to approximate sine (edit - njuffa commented that fdlibm uses a minimax polynomial approximation). The code defines the coefficients of the polynomial as S1 through S6. I decided to check the values of these coefficients, and found that S6 is only correct to one significant digit! I would expect it to be 1/(13!), which Windows Calculator and Google Calc tell me is 1.6059044...e-10, not 1.58969099521155010221e-10 (which is the value for S6 in the code). Even S5 differs in the fifth digit from 1/(11!). Can someone explain this discrepancy? Specifically, how are those coefficients (S1 through S6) determined?
/* #(#)k_sin.c 1.3 95/01/18 */
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunSoft, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
/* __kernel_sin( x, y, iy)
* kernel sin function on [-pi/4, pi/4], pi/4 ~ 0.7854
* Input x is assumed to be bounded by ~pi/4 in magnitude.
* Input y is the tail of x.
* Input iy indicates whether y is 0. (if iy=0, y assume to be 0).
*
* Algorithm
* 1. Since sin(-x) = -sin(x), we need only to consider positive x.
* 2. if x < 2^-27 (hx<0x3e400000 0), return x with inexact if x!=0.
* 3. sin(x) is approximated by a polynomial of degree 13 on
* [0,pi/4]
* 3 13
* sin(x) ~ x + S1*x + ... + S6*x
* where
*
* |sin(x) 2 4 6 8 10 12 | -58
* |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2
* | x |
*
* 4. sin(x+y) = sin(x) + sin'(x')*y
* ~ sin(x) + (1-x*x/2)*y
* For better accuracy, let
* 3 2 2 2 2
* r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6))))
* then 3 2
* sin(x) = x + (S1*x + (x *(r-y/2)+y))
*/
#include "fdlibm.h"
#ifdef __STDC__
static const double
#else
static double
#endif
half = 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */
S1 = -1.66666666666666324348e-01, /* 0xBFC55555, 0x55555549 */
S2 = 8.33333333332248946124e-03, /* 0x3F811111, 0x1110F8A6 */
S3 = -1.98412698298579493134e-04, /* 0xBF2A01A0, 0x19C161D5 */
S4 = 2.75573137070700676789e-06, /* 0x3EC71DE3, 0x57B1FE7D */
S5 = -2.50507602534068634195e-08, /* 0xBE5AE5E6, 0x8A2B9CEB */
S6 = 1.58969099521155010221e-10; /* 0x3DE5D93A, 0x5ACFD57C */
#ifdef __STDC__
double __kernel_sin(double x, double y, int iy)
#else
double __kernel_sin(x, y, iy)
double x,y; int iy; /* iy=0 if y is zero */
#endif
{
double z,r,v;
int ix;
ix = __HI(x)&0x7fffffff; /* high word of x */
if(ix<0x3e400000) /* |x| < 2**-27 */
{if((int)x==0) return x;} /* generate inexact */
z = x*x;
v = z*x;
r = S2+z*(S3+z*(S4+z*(S5+z*S6)));
if(iy==0) return x+v*(S1+z*r);
else return x-((z*(half*y-v*r)-y)-v*S1);
}
We can use trig identities to get everything down to 0≤x≤π/4, and then need a way to approximate sin x on that interval. On 0≤x≤2-27, we can just stick with sin x≈x (which the Taylor polynomial would also give, within the tolerance of a double).
The reason for not using a Taylor polynomial is in step 3 of the algorithm's comment. The Taylor polynomial gives (provable) accuracy near zero at the expense of less accuracy as you get away from zero. By the time you get to π/4, the 13th order Taylor polynomial (divided by x) differs from (sin x)/x by 3e-14. This is far worse than fblibm’s error of 2-58. In order to get that accurate with a Taylor polynomial, you’d need to go until (π/4)n-1/n! < 2-58, which takes another 2 or 3 terms.
So why does fblibm settle for an accuracy of 2-58? Because that’s past the tolerance of a double (which only has 52 bits in its mantissa).
In your case though, you’re wanting arbitrarily many bits of sin x. To use fblibm’s approach, you’d need to recalculate the coefficients whenever your desired accuracy changes. Your best approach seems to be to stick with the Taylor polynomial at 0, since it’s very easily computable, and take terms until (π/4)n-1/n! meets your desired accuracy.
njuffa had a useful idea of using identities to further restrict your domain. For example, sin(x) = 3*sin(x/3) - 4*sin^3(x/3). Using this would let you restrict your domain to 0≤x≤π/12. And you could use it twice to restrict your domain to 0≤x≤π/36. This would make it so that your Taylor expansion would have your desired accuracy much more quickly. And instead of trying to get an arbitrarily accurate value of π for (π/4)n-1/n!, I’d recommend rounding π up to 4 and going until 1/n! meets your desired accuracy (or 3-n/n! or 9-n/n! if you’ve used the trig identity once or twice).
Related
So I am making a fitness app for android and now I am asking the user to input a number e.g. 72.5
I would take this number and take percentages of it and apply functions to this etc.
I need to make sure that the percentage I take of that number is rounded to 2.5. This is because in a UK gym you only have the following plates: 1.25x2=2.5 2.5x2=5 5+2.5=7.5 , 10, 15, 20, 25
What I mean is that it would be numbers like these: 40, 42.5, 45, 47.5, 50
How can I round a Number N to the nearest 2.5? I understand that math.Round() rounds to nearest whole but what about to a custom number like this?
Do it as follows:
public class Main {
public static void main(String args[]) {
// Tests
System.out.println(roundToNearest2Point5(12));
System.out.println(roundToNearest2Point5(14));
System.out.println(roundToNearest2Point5(13));
System.out.println(roundToNearest2Point5(11));
}
static double roundToNearest2Point5(double n) {
return Math.round(n * 0.4) / 0.4;
}
}
Output:
12.5
15.0
12.5
10.0
Explanation:
It will be easier to understand with the following example:
double n = 20 / 3.0;
System.out.println(n);
System.out.println(Math.round(n));
System.out.println(Math.round(n * 100.0));
System.out.println(Math.round(n * 100.0) / 100.0);
Output:
6.666666666666667
7
667
6.67
As you can see here, rounding 20 / 3.0 returns 7 (which is the floor value after adding 0.5 to 20 / 3.0. Check this to understand the implementation). However, if you wanted to round it up to the nearest 1/100th place (i.e. up to 2 decimal places), an easier way (but not so precise. Check this for more precise way) would be to round n * 100.0 (which would make it 667) and then divide it by 100.0 which would give 6.67 (i.e. up to 2 decimal places). Note that 1 / (1 / 100.0) = 100.0
Similarly, to round the number to the nearest 2.5th place, you will need to do the same thing with 1 / 2.5 = 0.4 i.e. Math.round(n * 0.4) / 0.4.
To round a number to the nearest 100th place, you will need to do the same thing with 1 / 100 = 0.01 i.e. Math.round(n * 0.1) / 0.1.
To round a number to the nearest 0.5th place, you will need to do the same thing with 1 / 0.5 = 2.0 i.e. Math.round(n * 2.0) / 2.0.
I hope, it is clear.
Late reply but you can also do 2.5*(Math.round(number/2.5))
Same way if you wanted to round in lbs to the nearest 5th its 5*(Math.round(number/5))
I was wondering whether there is a Java function, either built-in to Java, or in an "offical" library such as Apache Commons Math, which computes the Gini coefficient.
From Wikipedia → Gini coefficient:
In economics, the Gini coefficient, sometimes called the Gini index or Gini ratio, is a measure of statistical dispersion intended to represent the income or wealth distribution of a nation's residents, and is the most commonly used measurement of inequality.
I'm not aware of one. But then writing one is pretty trivial!
double gini(List<Double> values) {
double sumOfDifference = values.stream()
.flatMapToDouble(v1 -> values.stream().mapToDouble(v2 -> Math.abs(v1 - v2))).sum();
double mean = values.stream().mapToDouble(v -> v).average().getAsDouble();
return sumOfDifference / (2 * values.size() * values.size() * mean);
}
I'm implementing the calculations of the book Practical Astronomy with your Calculator or Spreadsheet. Until now my calculations yield exactly the same result as the example calculations in the book.
However, arriving at §39 "Calculating corrections for parallax" I encounter a difference that I fail to understand.
The task at hand is described as follows:
As an example, let us calculate the apparent right ascension and declination of the Moon on 26
February 1979 at 16h 45m UT when observed from a location 60 metres above sea-level on longitude 100° W and latitude 50° N. The geocentric coordinates were α = 22h 35m 19s and δ = −7° 41′ 13′′, and the Moon’s equatorial horizontal parallax was 1° 01′ 09′′.
The book describes the sequence of calculation as follows:
Yet my outcome of step 7 is −31.993415, but book says −31,994415. If I do the mathematics of step 7 with the values of the book on a calculator the result is −31.993415 too, so my outcome seems to be right and the book's wrong....
I could live with that but there is a difference in step 10 too. My result is -8,570634, the books result is -8.538165, a rather large difference. I've read over step 10 time and time again to see if there is an error in my code, but I don't see it.
As until now my calculations and the books calculations are exactly the same I'm stuck. Am I doing something wrong (preferred), or did the book make an error (let's hope there aren't any more...)
My Java code for this function is as follows:
static EquatorialCoordinate parallax(EquatorialCoordinate body, ObserverLocation observer, ZonedDateTime zdt, double P) {
double Hd = 15d * raha(body.α, zdt, observer.λ);
step("α", body.α);
step("δ", body.δ);
step("φ", observer.φ);
step("λ", observer.λ);
step("h", observer.h);
step("H", Hd);
double H = toRadians(Hd);
Parallax ρ = parallax(observer.φ, observer.h);
step("P", P);
P = toRadians(P);
double δ = toRadians(body.δ);
double r = 1d / sin(P);
step("r", r);
double ρsinφ = ρ.sin;
double ρcosφ = ρ.cos;
step("ρcosφ'",ρcosφ);
step("ρsinφ'",ρsinφ);
double Δ = atan((ρcosφ * sin(H)) / ((r * cos(δ)) - (ρcosφ * cos(H))));
step("Δ", toDegrees(Δ));
H += Δ;
step("H'", toDegrees(H));
Δ = toDegrees(Δ);
double α$ = body.α - (Δ / 15d);
step("α'", α$);
double divident = (r * sin(δ)) - ρsinφ;
double divisor = ( r * cos(δ) * cos(H) ) - ρcosφ;
double δ$ = atan(cos(H) * (divident / divisor));
δ$ = toDegrees(δ$);
step("δ'", δ$);
return new EquatorialCoordinate(α$, δ$);
}
The "step" function does a simple formatted printf. The output of this program is:
α 22.588611
δ -7.686944
φ 50.000000
λ -100.000000
h 60.000000
H -31.642500
P 1.019167
r 56.221228
ρcosφ' 0.644060
ρsinφ' 0.762422
Δ -0.350915
H' -31.993414
α' 22.612005
δ' -8.570634
The resulting δ' is -8° 34' 14.28" instead of -8° 32' 17"
I've replaced my calculated value of H' with the books value to see if the book contains a carried error, but even if I do so the value is wrong.
Thus... my big question is, is my implementation wrong (and where, I can't see it), or were the books calculations wrong.
(Edit:) Class is annotated with strictfp, using java.util.StrictMath.
You write
H += Δ;
Which changes the value of H.
Then you write
double δ$ = atan(cos(H) * (divident / divisor));
Which uses the new version of H when it should use the old value.
Thanks to #svasa, I found that the divisor of step 10 should contain H, not H'.
The correct code is:
static EquatorialCoordinate parallax(EquatorialCoordinate body, ObserverLocation observer, ZonedDateTime zdt, double P) {
double H = toRadians(15d * raha(body.α, zdt, observer.λ));
P = toRadians(P);
Parallax ρ = parallax(observer.φ, observer.h);
double δ = toRadians(body.δ);
double r = 1d / sin(P);
double Δ = atan((ρ.cosφ * sin(H)) / ((r * cos(δ)) - (ρ.cosφ * cos(H))));
double H$ = H + Δ;
double α$ = body.α - (toDegrees(Δ) / 15d);
double δ$ = toDegrees(atan(cos(H$) * ((r * sin(δ) - ρ.sinφ) / (r * cos(δ) * cos(H) - ρ.cosφ))));
return new EquatorialCoordinate(α$, δ$);
}
I am trying to use java.lang.Math.IEEEremainder(double f1, double f2) in GWT. But I got below exception.
[ERROR] Line 1119: The method IEEEremainder(double, double) is
undefined for the type Math
I attempted to execute this code : angle = Math.IEEEremainder(angle, 360.0);
How to solve this issue in GWT?. If its not solve then what would be the alternative way to achieve the same functionality of Math.IEEEremainder this method.
According to the JRE Emulation this function is not supported in GWT.
So if you really need it and can't work around it, you will need to implement it yourself.
If I understand it correctly, you are trying to limit the angle to 360 degrees.
You could achieve that with this code:
/**
* Normalize a degree value.
* #param d value
* #return 0<=value<=360
*/
public static double normalizeDegrees(double d)
{
while (d < 0)
{
d += 360;
}
while (d > 360)
{
d -= 360;
}
return d;
}
If you just got positive numbers, you can even skip the upper while-Block.
If you really need to have the IEEEremainder method in GWT, implement it like that:
/**
* Computes the remainder operation on two arguments as prescribed by the IEEE 754 standard. The remainder value is
* mathematically equal to <code>f1 - f2</code> × <i>n</i>, where <i>n</i> is the
* mathematical integer closest to the exact mathematical value of the quotient {#code f1/f2}, and if two
* mathematical integers are equally close to {#code f1/f2}, then <i>n</i> is the integer that is even. If the
* remainder is zero, its sign is the same as the sign of the first argument. Special cases:
* <ul>
* <li>If either argument is NaN, or the first argument is infinite, or the second argument is positive zero or
* negative zero, then the result is NaN.
* <li>If the first argument is finite and the second argument is infinite, then the result is the same as the first
* argument.
* </ul>
* #param f1 the dividend.
* #param f2 the divisor.
* #return the remainder when {#code f1} is divided by {#code f2}.
*/
public static double IEEEremainder(double f1, double f2)
{
double div = Math.round(f1 / f2);
return f1 - (div * f2);
}
(I added this as a new comment to show the syntax highlighting).
So this project is a little outside of my comfort zone. I would describe my current stage of development as being one in which, “I know about things like: collection, design patters, and in general what makes for good OOP. But these things are sort of at my current limits. And so I probably don’t use them or attempt to use them as much as I should.”
I’m trying to change that, so I've been working on fairly small challenge/application that really lends itself to the above and asking myself to write smart, clean code. I’m fairly happy with what I've been able to do so far. However, I have two classes remaining that I still need to dive into. I have a lot of ideas about how to go about this. I’m sure some are good and some are bad, but more than anything, I think I’m over thinking things.
In short, here is what I’m trying to do, here is what I have, and here is where I need to go:
What I’m trying to do: The simplest way to state the goals for this app are, I have credit card (this class is the class I have done), I have wallets, and I have people. Looking at it from a high-level perspective, I’m putting cards in the wallets and wallets in the people. I have 3 cards, they really only differ in their ‘names’ and interest rates. I want some wallets to have 1 card and for others to have all three. As for wallets clearly every person needs at least one, but I’d like to give someone two. And that is really about it, I worked out some math for simple interest on the card which I’ll tie in at some point, but mostly I’m looking to build a clean and well-designed app.
What I have: As I've stated I more or less have the CreditCard class done. I’m still fine tuning it, but I've been able to improve it a lot and I’m casually happy with it. I’ll include this class below to provide context for the app and also, so you can provide suggestions if needed. At the top of the class you’ll see a lot of documentation. This is mostly just the math, and it’s logic, for working out simple interest. You’ll see. But I also have two test cases that I’m coding to, you’ll see this too.
Where I need to go: Well I have credit cards. Now I just need wallets and people. From my perspective, I could see the wallet making use of an ArrayList. Though, it could be the case that a different aspect of collections might serve better. I have mostly(mostly) used ArrayList, and so I keep mostly using ArrayList. It’s worked out so far… Beyond that, I have been considering making Wallet and Person abstract, which seems like a good idea, but once again, not much experience in making these choices.
So at the end of all of this, I’m look for some direction, conformation of good ideas and alternatives to weaker ones. If these could be combine with examples or if suggestions could express themselves in both words and code, this would be optimal because I get a lot more out of advice when I can see it in action. For me an OK suggestion with code, is ‘generally’ more helpful than a great suggestion without. It’s all about being able to apply that advice. But, that’s just me and everyone is different. What I can tell you, that is definite, is that all suggestion, whatever they are, will be appreciated and helpful. Because, I’m doing this, I’m here, to learn.
/*
* Test Cases:
* 1) 1 person has 1 wallet and 3 cards (1 Visa, 1 MC 1 Discover) – Each Card has a balance of $100 – calculate the total interest (simple interest) for this person and per card.
*
* 2) 1 person has 2 wallets Wallet 1 has a Visa and Discover , wallet 2 a MC - each card has $100
* balance - calculate the total interest(simple interest) for this person and interest per wallet
*/
/*
* Formula Key:
*
* Algebraic Symbols:
* A = Total Accrued Amount (principal + interest)
* P = Principal Amount
* I = Interest Amount
* r & R = Rate of Interest per year in percentage & decimal
* t = Time Period involved in months or years(duration pertaining to this equation)
*
* Rate of Interest, Percentage To Decimal Equations:
* R = r * 100
* r = R / 100
*
* Simple Interest Equation:
* A = P(1 + (r * t))
*/
/*
* Card:
* VISA 10%
*
* Equation:
* Accrued Amount(A) = Principle Amount(P) * (1 +(Interest Rate(r) * Time Period(t)))
*
* Calculation:
* First, converting Interest Rate(R) of 10%, to, Interest Rate(r) of 0.1
* r = R/100 = 10%/100 = 0.1 per year,
* put Time Period(t) of 1 month into years,
* months/year(1 month ÷ 12) = 0.08 years
*
* Solving Equation:
* A = 100(1 + (0.1 × 0.08)) = 100.8
* A = $ 100.80
*
* Solution:
* The total Amount Accrued(A), Principal(P) plus Interest(I),
* from Simple Interest on a Principal(P) of $ 100.00
* at a Rate(r = R/100(convert a percentage to a decimal)) of 10% or 0.1 per year
* for 0.08 years, 1 month(t) is $ 100.80.
*/
/*
* Card:
* MC(Master Card) 5%
*
* Equation:
* Accrued Amount(A) = Principle Amount(P) * (1 +(Interest Rate(r) * Time Period(t)))
*
* Calculation:
* First, converting Interest Rate(R) of 5%, to, Interest Rate(r) of 0.05
* r = R/100 = 5%/100 = 0.05 per year,
* put Time Period(t) of 1 month into years,
* months/year(1 month ÷ 12) = 0.08 years
*
* Solving Equation:
* A = 100(1 + (0.05 × 0.08)) = 100.4
* A = $ 100.40
*
* Solution:
* The total Amount Accrued(A), Principal(P) plus Interest(I),
* from Simple Interest on a Principal(P) of $ 100.00
* at a Rate(r = R/100(convert a percentage to a decimal)) of 5% or 0.05 per year
* for 0.08 years, 1 month(t) is $ 100.40.
*/
/*
* Card:
* Discover 1%
*
* Equation:
* Accrued Amount(A) = Principle Amount(P) * (1 +(Interest Rate(r) * Time Period(t)))
*
* Calculation:
* First, converting Interest Rate(R) of 1%, to, Interest Rate(r) of 0.01
* r = R/100 = 1%/100 = 0.01 per year,
* put Time Period(t) into years,
* months/year(1 month ÷ 12) = 0.08 years
*
*
* Solving Equation:
* A = 100(1 + (0.01 × 0.08)) = 100.08
* A = $ 100.08
*
* Solution:
* The total Amount Accrued(A), Principal(P) Plus Interest(I),
* from Simple Interest on a Principal(P) of $ 100.00
* at a Rate(r = R/100(convert a percentage to a decimal)) of 1% or 0.01 per year
* for 0.08 years, 1 month(t) is $ 100.08.
*/
public class CreditCard
{
private BrandOfCard brandOfCard;
private static final double PRINCIPAL_AMOUNT = 100.00;
private static final double TIME_PERIOD = 0.08;
public CreditCard(BrandOfCard brandOfCard)
{
this.brandOfCard = brandOfCard;
}
/*
* A = P(1 + (r * t))
*/
public double getAccruedAmount()
{
double accruedAmount;
accruedAmount = PRINCIPAL_AMOUNT * (1 + (brandOfCard.getInterestRate() * TIME_PERIOD));
return accruedAmount;
}
public enum BrandOfCard
{
VISA(0.1), MASTER_CARD(0.05), DISCOVER(0.01);
private final double interestRate;
BrandOfCard(double interestRate)
{
this.interestRate = interestRate;
}
public double getInterestRate()
{
return interestRate;
}
}
//bottom of class
public static void main(String[] args)
{
CreditCard visa = new CreditCard(BrandOfCard.VISA);
CreditCard masterCard = new CreditCard(BrandOfCard.MASTER_CARD);
CreditCard discover = new CreditCard(BrandOfCard.DISCOVER);
double accruedAmount;
accruedAmount = visa.getAccruedAmount();
System.out.println("Visa card, with a principle amount of $100.00, & an interest rate of 10%, " +
"has accrued $" + (accruedAmount - PRINCIPAL_AMOUNT) + " in interest, " +
"over the last monthly term.");
System.out.println("The total amount due on this card is now $" + accruedAmount);
accruedAmount = masterCard.getAccruedAmount();
System.out.println("Master Card card, with a principle amount of $100.00, & an interest rate of 5%, " +
"has accrued $" + (accruedAmount - PRINCIPAL_AMOUNT) + " in interest, " +
"over the last monthly term.");
System.out.println("The total amount due on this card is now $" + accruedAmount);
accruedAmount = discover.getAccruedAmount();
System.out.println("Discover card, with a principle amount of $100.00, & an interest rate of 1%, " +
"has accrued $" + (accruedAmount - PRINCIPAL_AMOUNT) + " in interest, " +
"over the last monthly term.");
System.out.println("The total amount due on this card is now $" + accruedAmount);
}
}
First of all don't go object happy! More objects != better code!
Look at this from a data perspective because that is where most OOP programmers lose their way. This stuff is stored relationally for a reason.
People make that an object
Wallet - This is simply a middle key to join n number of cards to a person.
CC make that an object since each CC has defined terms of payment, fees, interest rates, etc.
What you end up with is:
Table of users.
Table of cards.
The wallet, unless you want to assign particular attributes to it is really a non existent thing, since having the card owner Key in the CC record links cards to owners.
The rub is really applying payments. Just like the bank adds your deposits to your account before they start processing your checks or auto payments, you have to apply any posted payments before you calc the interest + fees value to add to the balance so:
for owners{
for cards with card.ownerID = owners.ID{
card.balance=card.balance-payments ;
card.balance=card.balance+calcInterest(card.balance, card.rate)+GetSumOfFees(card);
}
}
That is the basic batch job that every CC Issuer runs every night.
On the charging side of things it is the smallest code they can get away with sine this has to happen practically instantaneously at a pay terminal, etc. etc.
public static String chargeThis(String CCData, Double AMT){
CCNum = GetCCNum(CCData) ;
boolean isValid = validateCC(ccData);
if(isValid) return chargeCC(ccNum,AMT) else return rejectedTrans ;
}
About lists hash maps, etc in Java or vectors in C++...
ANY operation on those that sizes them will be blazingly fast right up until the point they exceed the size of the L2 cache! After they exceed the size of L2 cache they get stored in system RAM and their performance goes into the tank. At this point a linked list is superior, since adding or subtracting an element is simply inserting a node, or deleting a node.
Remember, getting advanced performance requires understanding the machine AND understanding how the JVM or compiler arranges things. In order from best to worst storage is as follows:
L0 cache - 1 cpu cycle
L1 cache - 2 cpu cycles
L2 cache - 5 to 10 cpu cycles
System Memory 1000's of cpu cycles
Ethernet ( getting data across the wire ) 10's of K cycles
Disk - 50 to 100 K cycles