spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
commit
message
[errors] refactoring errors
author
Ben Vogt <[email protected]>
date
2017-04-02 20:29:59
stats
12 file(s) changed, 208 insertions(+), 166 deletions(-)
files
README.md
src/Errors.ts
src/RawFormulas/Date.ts
src/RawFormulas/Engineering.ts
src/RawFormulas/Financial.ts
src/RawFormulas/Logical.ts
src/RawFormulas/Math.ts
src/RawFormulas/Statistical.ts
src/RawFormulas/Text.ts
src/RawFormulas/Utils.ts
tests/DateFormulasTest.ts
tests/FormulasTest.ts
   1diff --git a/README.md b/README.md
   2index 1694a13..5b95a00 100644
   3--- a/README.md
   4+++ b/README.md
   5@@ -16,9 +16,6 @@ And the same for MAX, MAXA, COUNT, COUNTA, etc. Look these over.
   6 * COVARIANCES
   7 * ...etc.
   8 
   9-### Refactor the way we construct and throw errors
  10-For example, the mis-matched argument length errors are all generated the same way.
  11-
  12 ### Refactor the way tests are organized.
  13 Group by error type and have some useful functions that will call with 0, N, N+1 args to test the args
  14 checker. Also, test for *all possible* errors that could be thrown, and *all possible types* that could be passed in.
  15@@ -41,9 +38,6 @@ Like dollars, dates are special types, but can be compared as if they're primati
  16 valid inside a cell: `=DATE(1992, 6, 6) > =DATE(1992, 6, 10)`. We should check types and and have Date-to-number
  17 conversion inside parser.js.
  18 
  19-* Organize tests in a way that makes sense.
  20-Annotate them, and standardize the error checking for errors like REF, NA, NUM, VALUE, etc.
  21-
  22 * Test all ExcelDate functions
  23 Right now we're just using the number of days since 1900, but we should check the other functions.
  24 
  25diff --git a/src/Errors.ts b/src/Errors.ts
  26index 13238c9..9398717 100644
  27--- a/src/Errors.ts
  28+++ b/src/Errors.ts
  29@@ -1,13 +1,3 @@
  30-class CellError extends Error {
  31-  public message: string;
  32-  public text: string;
  33-  constructor(message: string, text: string) {
  34-    super(message);
  35-    this.message = message;
  36-    this.text = text;
  37-  }
  38-}
  39-
  40 var NULL_ERROR = "#NULL!";
  41 var DIV_ZERO_ERROR = "#DIV/0!";
  42 var VALUE_ERROR = "#VALUE!";
  43@@ -44,14 +34,70 @@ var Errors = {
  44   }
  45 };
  46 
  47+
  48+class NullError extends Error {
  49+  constructor(message: string) {
  50+    super(message);
  51+    this.name = NULL_ERROR;
  52+  }
  53+}
  54+
  55+class DivZeroError extends Error {
  56+  constructor(message: string) {
  57+    super(message);
  58+    this.name = DIV_ZERO_ERROR;
  59+  }
  60+}
  61+
  62+class ValueError extends Error {
  63+  constructor(message: string) {
  64+    super(message);
  65+    this.name = VALUE_ERROR;
  66+  }
  67+}
  68+
  69+class RefError extends Error {
  70+  constructor(message: string) {
  71+    super(message);
  72+    this.name = REF_ERROR;
  73+  }
  74+}
  75+
  76+class NameError extends Error {
  77+  constructor(message: string) {
  78+    super(message);
  79+    this.name = NAME_ERROR;
  80+  }
  81+}
  82+
  83+class NumError extends Error {
  84+  constructor(message: string) {
  85+    super(message);
  86+    this.name = NUM_ERROR;
  87+  }
  88+}
  89+
  90+class NAError extends Error {
  91+  constructor(message: string) {
  92+    super(message);
  93+    this.name = NA_ERROR;
  94+  }
  95+}
  96+
  97 export {
  98   Errors,
  99-  CellError,
 100   DIV_ZERO_ERROR,
 101   NULL_ERROR,
 102   VALUE_ERROR,
 103   REF_ERROR,
 104   NAME_ERROR,
 105   NUM_ERROR,
 106-  NA_ERROR
 107+  NA_ERROR,
 108+  DivZeroError,
 109+  NullError,
 110+  ValueError,
 111+  RefError,
 112+  NameError,
 113+  NumError,
 114+  NAError
 115 }
 116\ No newline at end of file
 117diff --git a/src/RawFormulas/Date.ts b/src/RawFormulas/Date.ts
 118index a782a89..587cc82 100644
 119--- a/src/RawFormulas/Date.ts
 120+++ b/src/RawFormulas/Date.ts
 121@@ -3,13 +3,10 @@ import * as moment from "moment";
 122 import * as Formula from "formulajs"
 123 import {
 124   ArgsChecker,
 125-  DateRegExBuilder,
 126   TypeCaster
 127 } from "./Utils";
 128 import {
 129-  NUM_ERROR,
 130-  VALUE_ERROR,
 131-  CellError
 132+  NumError, ValueError
 133 } from "../Errors";
 134 import {
 135   ExcelDate,
 136@@ -37,7 +34,7 @@ var DATE = function (...values) : ExcelDate {
 137       .add(day, 'days');
 138   var excelDate = new ExcelDate(m);
 139   if (excelDate.toNumber() < 0) {
 140-    throw new CellError(NUM_ERROR, "DATE evaluates to an out of range value " + excelDate.toNumber()
 141+    throw new NumError("DATE evaluates to an out of range value " + excelDate.toNumber()
 142         + ". It should be greater than or equal to 0.");
 143   }
 144   return excelDate;
 145@@ -58,7 +55,7 @@ var DATEVALUE = function (...values) : number {
 146   try {
 147     date = TypeCaster.stringToExcelDate(dateString);
 148   } catch (e) {
 149-    throw new CellError(VALUE_ERROR, "DATEVALUE parameter '" + dateString + "' cannot be parsed to date/time.");
 150+    throw new ValueError("DATEVALUE parameter '" + dateString + "' cannot be parsed to date/time.");
 151   }
 152 
 153   // If we've not been able to parse the date by now, then we cannot parse it at all.
 154diff --git a/src/RawFormulas/Engineering.ts b/src/RawFormulas/Engineering.ts
 155index 576f392..72bbea5 100644
 156--- a/src/RawFormulas/Engineering.ts
 157+++ b/src/RawFormulas/Engineering.ts
 158@@ -2,8 +2,10 @@ import {
 159   ArgsChecker,
 160   TypeCaster
 161 } from "./Utils";
 162-import { CellError } from "../Errors"
 163-import * as ERRORS from "../Errors"
 164+import {
 165+  ValueError,
 166+  NumError
 167+} from "../Errors"
 168 
 169 /**
 170  * Converts a signed binary number to decimal format.
 171@@ -16,11 +18,11 @@ import * as ERRORS from "../Errors"
 172 var BIN2DEC = function (...values) : number {
 173   ArgsChecker.checkLength(values, 1);
 174   if (typeof TypeCaster.firstValue(values[0]) === "boolean") {
 175-    throw new CellError(ERRORS.VALUE_ERROR, "Function BIN2DEC parameter 1 expects text values. But '" + values[0] + "' is a boolean and cannot be coerced to a text.");
 176+    throw new ValueError("Function BIN2DEC parameter 1 expects text values. But '" + values[0] + "' is a boolean and cannot be coerced to a text.");
 177   }
 178   var n = TypeCaster.firstValueAsString(values[0]);
 179   if (!(/^[01]{1,10}$/).test(n)) {
 180-    throw new CellError(ERRORS.NUM_ERROR, "Input for BIN2DEC ('"+n+"') is not a valid binary representation.");
 181+    throw new NumError("Input for BIN2DEC ('" + n + "') is not a valid binary representation.");
 182   }
 183 
 184   if (n.length === 10 && n.substring(0, 1) === '1') {
 185@@ -42,7 +44,7 @@ var BIN2DEC = function (...values) : number {
 186 var BIN2HEX = function (...values) : string {
 187   ArgsChecker.checkLengthWithin(values, 1, 2);
 188   if (typeof TypeCaster.firstValue(values[0]) === "boolean") {
 189-    throw new CellError(ERRORS.VALUE_ERROR, "Function BIN2HEX parameter 1 expects text values. But '" + values[0] + "' is a boolean and cannot be coerced to a text.");
 190+    throw new ValueError("Function BIN2HEX parameter 1 expects text values. But '" + values[0] + "' is a boolean and cannot be coerced to a text.");
 191   }
 192   var n = TypeCaster.firstValueAsString(values[0]);
 193   var p = 10;
 194@@ -50,7 +52,7 @@ var BIN2HEX = function (...values) : string {
 195     p = TypeCaster.firstValueAsNumber(values[1]);
 196   }
 197   if (!(/^[01]{1,10}$/).test(n)) {
 198-    throw new CellError(ERRORS.NUM_ERROR, "Input for BIN2HEX ('"+n+"') is not a valid binary representation.");
 199+    throw new NumError("Input for BIN2HEX ('"+n+"') is not a valid binary representation.");
 200   }
 201 
 202   if (n.length === 10 && n.substring(0, 1) === '1') {
 203@@ -58,7 +60,7 @@ var BIN2HEX = function (...values) : string {
 204   }
 205 
 206   if (p < 1 || p > 10) {
 207-    throw new CellError(ERRORS.NUM_ERROR, "Function BIN2HEX parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
 208+    throw new NumError("Function BIN2HEX parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
 209   }
 210   p = Math.floor(p);
 211   // Convert decimal number to hexadecimal
 212@@ -88,7 +90,7 @@ var BIN2HEX = function (...values) : string {
 213 var BIN2OCT = function (...values) : string {
 214   ArgsChecker.checkLengthWithin(values, 1, 2);
 215   if (typeof TypeCaster.firstValue(values[0]) === "boolean") {
 216-    throw new CellError(ERRORS.VALUE_ERROR, "Function BIN2OCT parameter 1 expects text values. But '" + values[0] + "' is a boolean and cannot be coerced to a text.");
 217+    throw new ValueError("Function BIN2OCT parameter 1 expects text values. But '" + values[0] + "' is a boolean and cannot be coerced to a text.");
 218   }
 219   var n = TypeCaster.firstValueAsString(values[0]);
 220   var p = 10;
 221@@ -96,7 +98,7 @@ var BIN2OCT = function (...values) : string {
 222     p = TypeCaster.firstValueAsNumber(values[1]);
 223   }
 224   if (!(/^[01]{1,10}$/).test(n)) {
 225-    throw new CellError(ERRORS.NUM_ERROR, "Input for BIN2OCT ('"+n+"') is not a valid binary representation.");
 226+    throw new NumError("Input for BIN2OCT ('"+n+"') is not a valid binary representation.");
 227   }
 228 
 229   if (n.length === 10 && n.substring(0, 1) === '1') {
 230@@ -104,7 +106,7 @@ var BIN2OCT = function (...values) : string {
 231   }
 232 
 233   if (p < 1 || p > 10) {
 234-    throw new CellError(ERRORS.NUM_ERROR, "Function BIN2OCT parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
 235+    throw new NumError("Function BIN2OCT parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
 236   }
 237   p = Math.floor(p);
 238   var result = parseInt(n.toString(), 2).toString(8);
 239@@ -146,10 +148,10 @@ var DEC2OCT = function (...values) : string {
 240     placesPresent = true;
 241   }
 242   if (n < -53687092 || n > 536870911) {
 243-    throw new CellError(ERRORS.NUM_ERROR, "Function DEC2OCT parameter 1 value is " + n + ". Valid values are between -53687092 and 536870911 inclusive.");
 244+    throw new NumError("Function DEC2OCT parameter 1 value is " + n + ". Valid values are between -53687092 and 536870911 inclusive.");
 245   }
 246   if (p < 1 || p > 10) {
 247-    throw new CellError(ERRORS.NUM_ERROR, "Function DEC2OCT parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
 248+    throw new NumError("Function DEC2OCT parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
 249   }
 250   if (n < 0) {
 251     return (1073741824 + n).toString(8).toUpperCase();
 252@@ -194,10 +196,10 @@ var DEC2HEX = function (...values) : string {
 253     placesPresent = true;
 254   }
 255   if (n < -549755813888 || n > 549755813887) {
 256-    throw new CellError(ERRORS.NUM_ERROR, "Function DEC2HEX parameter 1 value is " + n + ". Valid values are between -549755813888 and 549755813887 inclusive.");
 257+    throw new NumError("Function DEC2HEX parameter 1 value is " + n + ". Valid values are between -549755813888 and 549755813887 inclusive.");
 258   }
 259   if (p < 1 || p > 10) {
 260-    throw new CellError(ERRORS.NUM_ERROR, "Function DEC2HEX parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
 261+    throw new NumError("Function DEC2HEX parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
 262   }
 263   // Ignore places and return a 10-character hexadecimal number if number is negative
 264   if (n < 0) {
 265@@ -246,10 +248,10 @@ var DEC2BIN = function (...values) : string {
 266   }
 267 
 268   if (n < -512 || n > 511) {
 269-    throw new CellError(ERRORS.NUM_ERROR, "Function DEC2BIN parameter 1 value is " + n + ". Valid values are between -512 and 511 inclusive.");
 270+    throw new NumError("Function DEC2BIN parameter 1 value is " + n + ". Valid values are between -512 and 511 inclusive.");
 271   }
 272   if (p < 1 || p > 10) {
 273-    throw new CellError(ERRORS.NUM_ERROR, "Function DEC2BIN parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
 274+    throw new NumError("Function DEC2BIN parameter 2 value is " + p + ". Valid values are between 1 and 10 inclusive.");
 275   }
 276 
 277   // Ignore places and return a 10-character binary number if number is negative
 278diff --git a/src/RawFormulas/Financial.ts b/src/RawFormulas/Financial.ts
 279index 7dae7f2..af38ac7 100644
 280--- a/src/RawFormulas/Financial.ts
 281+++ b/src/RawFormulas/Financial.ts
 282@@ -4,9 +4,9 @@ import {
 283   checkForDevideByZero
 284 } from "./Utils";
 285 import {
 286-  CellError
 287+  NumError,
 288+  DivZeroError
 289 } from "../Errors"
 290-import * as ERRORS from "../Errors"
 291 
 292 /**
 293  * Calculates the depreciation of an asset for a specified period using the double-declining balance method.
 294@@ -27,23 +27,23 @@ var DDB = function (...values) : number {
 295   var factor = values.length === 5 ? TypeCaster.firstValueAsNumber(values[4]) : 2;
 296 
 297   if (cost < 0) {
 298-    throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 1 value is "
 299+    throw new NumError("Function DDB parameter 1 value is "
 300       + cost + ". It should be greater than or equal to 0.");
 301   }
 302   if (salvage < 0) {
 303-    throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 2 value is "
 304+    throw new NumError("Function DDB parameter 2 value is "
 305       + salvage + ". It should be greater than or equal to 0.");
 306   }
 307   if (life < 0) {
 308-    throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 3 value is "
 309+    throw new NumError("Function DDB parameter 3 value is "
 310       + life + ". It should be greater than or equal to 0.");
 311   }
 312   if (period < 0) {
 313-    throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 4 value is "
 314+    throw new NumError("Function DDB parameter 4 value is "
 315       + period + ". It should be greater than or equal to 0.");
 316   }
 317   if (period > life) {
 318-    throw new CellError(ERRORS.NUM_ERROR, "Function DDB parameter 4 value is "
 319+    throw new NumError("Function DDB parameter 4 value is "
 320       + life + ". It should be less than or equal to value of Function DB parameter 3 with "+ period +".");
 321   }
 322   if (salvage >= cost) {
 323@@ -78,34 +78,34 @@ var DB = function (...values) : number {
 324   var period = TypeCaster.firstValueAsNumber(values[3]);
 325   var month = values.length === 5 ? Math.floor(TypeCaster.firstValueAsNumber(values[4])) : 12;
 326   if (cost < 0) {
 327-    throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 1 value is "
 328+    throw new NumError("Function DB parameter 1 value is "
 329       + cost + ". It should be greater than or equal to 0.");
 330   }
 331   if (salvage < 0) {
 332-    throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 2 value is "
 333+    throw new NumError("Function DB parameter 2 value is "
 334       + salvage + ". It should be greater than or equal to 0.");
 335   }
 336   if (life < 0) {
 337-    throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 3 value is "
 338+    throw new NumError("Function DB parameter 3 value is "
 339       + life + ". It should be greater than or equal to 0.");
 340   }
 341   if (period < 0) {
 342-    throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 4 value is "
 343+    throw new NumError("Function DB parameter 4 value is "
 344       + period + ". It should be greater than or equal to 0.");
 345   }
 346   if (month > 12 || month < 1) {
 347-    throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 5 value is "
 348+    throw new NumError("Function DB parameter 5 value is "
 349       + month + ". Valid values are between 1 and 12 inclusive.");
 350   }
 351   if (period > life) {
 352-    throw new CellError(ERRORS.NUM_ERROR, "Function DB parameter 4 value is "
 353+    throw new NumError("Function DB parameter 4 value is "
 354       + life + ". It should be less than or equal to value of Function DB parameter 3 with "+ period +".");
 355   }
 356   if (salvage >= cost) {
 357     return 0;
 358   }
 359   if (cost === 0 && salvage !== 0) {
 360-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function DB cause a divide by zero error.")
 361+    throw new DivZeroError("Evaluation of function DB cause a divide by zero error.")
 362   }
 363   var rate = (1 - Math.pow(salvage / cost, 1 / life));
 364   var initial = cost * rate * month / 12;
 365@@ -149,7 +149,7 @@ var DOLLAR = function (...values) : number {
 366   var divisor = sign * (Math.floor(Math.abs(v) * Math.pow(10, places)));
 367   var pow = Math.pow(10, places);
 368   if (pow === 0 && divisor !== 0) {
 369-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function DOLLAR cause a divide by zero error.")
 370+    throw new DivZeroError("Evaluation of function DOLLAR cause a divide by zero error.")
 371   }
 372   return divisor / pow;
 373 };
 374@@ -167,13 +167,13 @@ var DOLLARDE = function (...values) : number {
 375   var dollar = TypeCaster.firstValueAsNumber(values[0]);
 376   var fraction = Math.floor(TypeCaster.firstValueAsNumber(values[1]));
 377   if (fraction === 0) {
 378-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Function DOLLARDE parameter 2 cannot be zero.");
 379+    throw new DivZeroError("Function DOLLARDE parameter 2 cannot be zero.");
 380   }
 381   var result = parseInt(dollar.toString(), 10);
 382   result += (dollar % 1) * Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN10)) / fraction;
 383   var power = Math.pow(10, Math.ceil(Math.log(fraction) / Math.LN2) + 1);
 384   if (power === 0) {
 385-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function DOLLARDE caused a divide by zero error.");
 386+    throw new DivZeroError("Evaluation of function DOLLARDE cause a divide by zero error.")
 387   }
 388   result = Math.round(result * power) / power;
 389   return result;
 390@@ -192,7 +192,7 @@ var DOLLARFR = function (...values) : number {
 391   var dollar = TypeCaster.firstValueAsNumber(values[0]);
 392   var unit = Math.floor(TypeCaster.firstValueAsNumber(values[1]));
 393   if (unit === 0) {
 394-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Function DOLLARFR parameter 2 cannot be zero.");
 395+    throw new DivZeroError("Function DOLLARFR parameter 2 cannot be zero.");
 396   }
 397   var result = parseInt(dollar.toString(), 10);
 398   result += (dollar % 1) * Math.pow(10, -Math.ceil(Math.log(unit) / Math.LN10)) * unit;
 399@@ -212,10 +212,10 @@ var EFFECT = function (...values) : number {
 400   var rate = TypeCaster.firstValueAsNumber(values[0]);
 401   var periods = TypeCaster.firstValueAsNumber(values[1]);
 402   if (rate <= 0) {
 403-    throw new CellError(ERRORS.NUM_ERROR, "Function EFFECT parameter 1 value is " + rate + ". It should be greater than to 0");
 404+    throw new NumError("Function EFFECT parameter 1 value is " + rate + ". It should be greater than to 0");
 405   }
 406   if (periods < 1) {
 407-    throw new CellError(ERRORS.NUM_ERROR, "Function EFFECT parameter 2 value is " + periods + ". It should be greater than or equal to 1");
 408+    throw new NumError("Function EFFECT parameter 2 value is " + periods + ". It should be greater than or equal to 1");
 409   }
 410   periods = Math.floor(periods);
 411   return Math.pow(1 + rate / periods, periods) - 1;
 412@@ -274,14 +274,14 @@ var CUMPRINC = function (...values) : number {
 413   var value = TypeCaster.firstValueAsNumber(values[2]);
 414   var start = TypeCaster.firstValueAsNumber(values[3]);
 415   if (start < 1) {
 416-    throw new CellError(ERRORS.NUM_ERROR, "Function CUMPRINC parameter 4 value is " + start + ". It should be greater than or equal to 1.");
 417+    throw new NumError("Function CUMPRINC parameter 4 value is " + start + ". It should be greater than or equal to 1.");
 418   }
 419   var end = TypeCaster.firstValueAsNumber(values[4]);
 420   if (end < 1) {
 421-    throw new CellError(ERRORS.NUM_ERROR, "Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to 1.");
 422+    throw new NumError("Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to 1.");
 423   }
 424   if (end < start) {
 425-    throw new CellError(ERRORS.NUM_ERROR, "Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to " + start + ".");
 426+    throw new NumError("Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to " + start + ".");
 427   }
 428   var type = TypeCaster.firstValueAsBoolean(values[5]);
 429 
 430@@ -326,14 +326,14 @@ var CUMIPMT = function (...values) : number {
 431   var value = TypeCaster.firstValueAsNumber(values[2]);
 432   var start = TypeCaster.firstValueAsNumber(values[3]);
 433   if (start < 1) {
 434-    throw new CellError(ERRORS.NUM_ERROR, "Function CUMPRINC parameter 4 value is " + start + ". It should be greater than or equal to 1.");
 435+    throw new NumError("Function CUMPRINC parameter 4 value is " + start + ". It should be greater than or equal to 1.");
 436   }
 437   var end = TypeCaster.firstValueAsNumber(values[4]);
 438   if (end < 1) {
 439-    throw new CellError(ERRORS.NUM_ERROR, "Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to 1.");
 440+    throw new NumError("Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to 1.");
 441   }
 442   if (end < start) {
 443-    throw new CellError(ERRORS.NUM_ERROR, "Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to " + start + ".");
 444+    throw new NumError("Function CUMPRINC parameter 5 value is " + end + ". It should be greater than or equal to " + start + ".");
 445   }
 446   var type = TypeCaster.firstValueAsBoolean(values[5]);
 447 
 448diff --git a/src/RawFormulas/Logical.ts b/src/RawFormulas/Logical.ts
 449index 28b2b5d..747acf8 100644
 450--- a/src/RawFormulas/Logical.ts
 451+++ b/src/RawFormulas/Logical.ts
 452@@ -1,6 +1,5 @@
 453 import { ArgsChecker, TypeCaster } from "./Utils"
 454-import { CellError } from "../Errors"
 455-import * as ERRORS from "../Errors"
 456+import { ValueError, RefError } from "../Errors"
 457 
 458 /**
 459  * Returns true if all of the provided arguments are logically true, and false if any of the provided arguments are logically false.
 460@@ -13,7 +12,7 @@ var AND = function (...values) {
 461   var result = true;
 462   for (var i = 0; i < values.length; i++) {
 463     if (typeof values[i] === "string") {
 464-      throw new CellError(ERRORS.VALUE_ERROR, "AND expects boolean values. But '" + values[i] + "' is a text and cannot be coerced to a boolean.")
 465+      throw new ValueError("AND expects boolean values. But '" + values[i] + "' is a text and cannot be coerced to a boolean.")
 466     } else if (values[i] instanceof Array) {
 467       if (!AND.apply(this, values[i])) {
 468         result = false;
 469@@ -40,12 +39,12 @@ var EXACT = function (...values) {
 470   var two = values[1];
 471   if (one instanceof Array) {
 472     if (one.length === 0) {
 473-      throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 474+      throw new RefError("Reference does not exist.");
 475     }
 476   }
 477   if (two instanceof Array) {
 478     if (two.length === 0) {
 479-      throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 480+      throw new RefError("Reference does not exist.");
 481     }
 482   }
 483   one = TypeCaster.valueToString(one);
 484@@ -87,14 +86,14 @@ var NOT = function (...values) : boolean {
 485     if (X === "") {
 486       return true;
 487     }
 488-    throw new CellError(ERRORS.VALUE_ERROR, "Function NOT parameter 1 expects boolean values. But '" + X + "' is a text and cannot be coerced to a boolean.")
 489+    throw new ValueError("Function NOT parameter 1 expects boolean values. But '" + X + "' is a text and cannot be coerced to a boolean.")
 490   }
 491   if (typeof(X) === "number") {
 492     return X === 0;
 493   }
 494   if (X instanceof Array) {
 495     if (X.length === 0) {
 496-      throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 497+      throw new RefError("Reference does not exist.");
 498     }
 499     return NOT(X[0]);
 500   }
 501@@ -112,7 +111,7 @@ var OR = function (...values) {
 502   for (var i = 0; i < values.length; i++) {
 503     if (values[i] instanceof Array) {
 504       if (values[i].length === 0) {
 505-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 506+        throw new RefError("Reference does not exist.");
 507       }
 508       if (OR.apply(this, values[i])) {
 509         return true;
 510@@ -136,7 +135,7 @@ var XOR = function (...values) {
 511   for (var i = 0; i < values.length; i++) {
 512     if (values[i] instanceof Array) {
 513       if (values[i].length === 0) {
 514-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 515+        throw new RefError("Reference does not exist.");
 516       }
 517       if (XOR.apply(this, values[i])) {
 518         if (alreadyTruthy) {
 519diff --git a/src/RawFormulas/Math.ts b/src/RawFormulas/Math.ts
 520index 9915b86..f01ef1a 100644
 521--- a/src/RawFormulas/Math.ts
 522+++ b/src/RawFormulas/Math.ts
 523@@ -6,9 +6,8 @@ import {
 524   TypeCaster
 525 } from "./Utils";
 526 import {
 527-  CellError
 528+  NumError, DivZeroError, RefError, ValueError, NAError
 529 } from "../Errors";
 530-import * as ERRORS from "../Errors";
 531 
 532 /**
 533  * Returns the absolute value of a number.
 534@@ -34,7 +33,7 @@ var ACOS = function (value?) {
 535   if (value === -1) {
 536     return Math.PI;
 537   } else if (value > 1 || value < -1) {
 538-    throw new CellError(ERRORS.NUM_ERROR, "Function ____ parameter 1 value is " + value + ". Valid values are between -1 and 1 inclusive.");
 539+    throw new NumError("Function ACOS parameter 1 value is " + value + ". Valid values are between -1 and 1 inclusive.");
 540   }
 541   return Math.acos(value);
 542 };
 543@@ -49,7 +48,7 @@ var ACOSH = function (value?) {
 544   ArgsChecker.checkLength(arguments, 1);
 545   value = TypeCaster.valueToNumber(value);
 546   if (value < 1) {
 547-    throw new CellError(ERRORS.NUM_ERROR, "Function ____ parameter 1 value is " + value + ". It should be greater than or equal to 1.");
 548+    throw new NumError("Function ACOSH parameter 1 value is " + value + ". It should be greater than or equal to 1.");
 549   }
 550   return Math.log(value + Math.sqrt(value * value - 1));
 551 };
 552@@ -64,7 +63,7 @@ var ACOTH = function (value?) {
 553   ArgsChecker.checkLength(arguments, 1);
 554   value = TypeCaster.valueToNumber(value);
 555   if (value <= 1 && value >= -1) {
 556-    throw new CellError(ERRORS.NUM_ERROR, "Function ____ parameter 1 value is " + value + ". Valid values cannot be between -1 and 1 inclusive.")
 557+    throw new NumError("Function ACOTH parameter 1 value is " + value + ". Valid values cannot be between -1 and 1 inclusive.")
 558   }
 559   return 0.5 * Math.log((value + 1) / (value - 1));
 560 };
 561@@ -81,7 +80,7 @@ var ASIN = function (value?) {
 562   if (value === -1) {
 563     return Math.PI;
 564   } else if (value > 1 || value < -1) {
 565-    throw new CellError(ERRORS.NUM_ERROR, "Function ____ parameter 1 value is " + value + ". Valid values are between -1 and 1 inclusive.");
 566+    throw new NumError("Function ASIN parameter 1 value is " + value + ". Valid values are between -1 and 1 inclusive.");
 567   }
 568   return Math.asin(value);
 569 };
 570@@ -111,7 +110,7 @@ var ATAN = function (value?) {
 571   if (value === -1) {
 572     return Math.PI;
 573   } else if (value > 1 || value < -1) {
 574-    throw new CellError(ERRORS.NUM_ERROR, "Function ____ parameter 1 value is " + value + ". Valid values are between -1 and 1 inclusive.");
 575+    throw new NumError("Function ATAN parameter 1 value is " + value + ". Valid values are between -1 and 1 inclusive.");
 576   }
 577   return Math.atan(value);
 578 };
 579@@ -129,7 +128,7 @@ var ATAN2 = function (x, y) {
 580   x = TypeCaster.valueToNumber(x);
 581   y = TypeCaster.valueToNumber(y);
 582   if (x === 0 && y === 0) {
 583-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function ATAN2 caused a divide by zero error.");
 584+    throw new DivZeroError("Evaluation of function ATAN2 caused a divide by zero error.");
 585   }
 586   return Math.atan2(y, x);
 587 };
 588@@ -145,7 +144,7 @@ var ATANH = function (value?) : number {
 589   ArgsChecker.checkLength(arguments, 1);
 590   value = TypeCaster.valueToNumber(value);
 591   if (value >= 1 || value <= -1) {
 592-    throw new CellError(ERRORS.NUM_ERROR, "Function ATANH parameter 1 value is " + value + ". Valid values are between -1 and 1 exclusive.");
 593+    throw new NumError("Function ATANH parameter 1 value is " + value + ". Valid values are between -1 and 1 exclusive.");
 594   }
 595   if (Math.abs(value) < 1) {
 596 
 597@@ -163,7 +162,7 @@ var EVEN = function (...values) : number {
 598   ArgsChecker.checkLength(values, 1);
 599   if (values[0] instanceof Array) {
 600     if (values[0].length === 0) {
 601-      throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 602+      throw new RefError("Reference does not exist.");
 603     }
 604     return EVEN(values[0][0]);
 605   }
 606@@ -183,7 +182,7 @@ var MOD = function (...values) : number {
 607   var oneN = TypeCaster.valueToNumber(values[0]);
 608   var twoN =  TypeCaster.valueToNumber(values[1]);
 609   if (twoN === 0) {
 610-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Function MOD parameter 2 cannot be zero.");
 611+    throw new DivZeroError("Function MOD parameter 2 cannot be zero.");
 612   }
 613   return oneN % twoN;
 614 };
 615@@ -199,7 +198,7 @@ var ODD = function (...values) : number {
 616   ArgsChecker.checkLength(values, 1);
 617   if (values[0] instanceof Array) {
 618     if (values[0].length === 0) {
 619-      throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 620+      throw new RefError("Reference does not exist.");
 621     }
 622     return ODD(values[0][0]);
 623   }
 624@@ -235,7 +234,7 @@ var SUM = function (...values) : number {
 625       result = result + SUM.apply(this, values[i]);
 626     } else {
 627       if (values[i] === "") {
 628-        throw new CellError(ERRORS.VALUE_ERROR, "Function SUM parameter "+i+" expects number values. But '"+values[i]+"' is a text and cannot be coerced to a number.");
 629+        throw new ValueError("Function SUM parameter "+i+" expects number values. But '"+values[i]+"' is a text and cannot be coerced to a number.");
 630       }
 631       result = result + TypeCaster.valueToNumber(values[i]);
 632     }
 633@@ -253,7 +252,7 @@ var SQRT = function (...values) : number {
 634   ArgsChecker.checkLength(values, 1);
 635   var x = TypeCaster.firstValueAsNumber(values[0]);
 636   if (x < 0) {
 637-    throw new CellError(ERRORS.VALUE_ERROR, "Function SQRT parameter 1 expects number values. But '" + values[0] + "' is a text and cannot be coerced to a number.");
 638+    throw new ValueError("Function SQRT parameter 1 expects number values. But '" + values[0] + "' is a text and cannot be coerced to a number.");
 639   }
 640   return Math.sqrt(x);
 641 };
 642@@ -268,7 +267,7 @@ var SQRTPI = function (...values) : number{
 643   ArgsChecker.checkLength(values, 1);
 644   var n = TypeCaster.firstValueAsNumber(values[0]);
 645   if (n < 0) {
 646-    throw new CellError(ERRORS.NUM_ERROR, "Function SQRTPI parameter 1 value is " + n + ". It should be greater than or equal to 0.");
 647+    throw new NumError("Function SQRTPI parameter 1 value is " + n + ". It should be greater than or equal to 0.");
 648   }
 649   return Math.sqrt(n * Math.PI);
 650 };
 651@@ -307,7 +306,7 @@ var COT = function (...values) : number {
 652   ArgsChecker.checkLength(values, 1);
 653   var x = TypeCaster.firstValueAsNumber(values[0]);
 654   if (x === 0) {
 655-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function COT caused a divide by zero error.");
 656+    throw new DivZeroError("Evaluation of function COT caused a divide by zero error.");
 657   }
 658   return 1 / Math.tan(x);
 659 };
 660@@ -322,7 +321,7 @@ var COTH = function (...values) : number {
 661   ArgsChecker.checkLength(values, 1);
 662   var x = TypeCaster.firstValueAsNumber(values[0]);
 663   if (x === 0) {
 664-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function COTH caused a divide by zero error.");
 665+    throw new DivZeroError("Evaluation of function COTH caused a divide by zero error.");
 666   }
 667   return 1 / Math["tanh"](x);
 668 };
 669@@ -349,7 +348,7 @@ var INT = function (...values) : number {
 670 var ISEVEN = function (...values) : boolean {
 671   ArgsChecker.checkLength(values, 1);
 672   if (values[0] === "") {
 673-    throw new CellError(ERRORS.VALUE_ERROR, "Function ISEVEN parameter 1 expects boolean values. But '" + values[0] + "' is a text and cannot be coerced to a boolean.");
 674+    throw new ValueError("Function ISEVEN parameter 1 expects boolean values. But '" + values[0] + "' is a text and cannot be coerced to a boolean.");
 675   }
 676   var x = TypeCaster.firstValueAsNumber(values[0]);
 677   return Math.floor(x) % 2 === 0;
 678@@ -365,7 +364,7 @@ var ISEVEN = function (...values) : boolean {
 679 var ISODD = function (...values) : boolean {
 680   ArgsChecker.checkLength(values, 1);
 681   if (values[0] === "") {
 682-    throw new CellError(ERRORS.VALUE_ERROR, "Function ISODD parameter 1 expects boolean values. But '" + values[0] + "' is a text and cannot be coerced to a boolean.");
 683+    throw new ValueError("Function ISODD parameter 1 expects boolean values. But '" + values[0] + "' is a text and cannot be coerced to a boolean.");
 684   }
 685   var x = TypeCaster.firstValueAsNumber(values[0]);
 686   return Math.floor(x) % 2 === 1;
 687@@ -414,7 +413,7 @@ var LOG10 = function (...values) : number {
 688   ArgsChecker.checkLength(values, 1);
 689   var n = TypeCaster.firstValueAsNumber(values[0]);
 690   if (n < 1) {
 691-    throw new CellError(ERRORS.NUM_ERROR, "Function LOG10 parameter 1 value is " + n + ". It should be greater than 0.");
 692+    throw new NumError("Function LOG10 parameter 1 value is " + n + ". It should be greater than 0.");
 693   }
 694   var ln = Math.log(n);
 695   var lb = Math.log(10);
 696@@ -435,16 +434,16 @@ var LOG = function (...values) : number {
 697   if (values.length > 1) {
 698     b = TypeCaster.firstValueAsNumber(values[1]);
 699     if (b < 1) {
 700-      throw new CellError(ERRORS.NUM_ERROR, "Function LOG parameter 2 value is " + b + ". It should be greater than 0.");
 701+      throw new NumError("Function LOG parameter 2 value is " + b + ". It should be greater than 0.");
 702     }
 703   }
 704   if (b < 2) {
 705-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function LOG caused a divide by zero error.");
 706+    throw new DivZeroError("Evaluation of function LOG caused a divide by zero error.");
 707   }
 708   var ln = Math.log(n);
 709   var lb = Math.log(b);
 710   if (lb === 0) {
 711-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function LOG caused a divide by zero error.");
 712+    throw new DivZeroError("Evaluation of function LOG caused a divide by zero error.");
 713   }
 714   return ln / lb;
 715 };
 716@@ -459,7 +458,7 @@ var LN = function (...values) : number {
 717   ArgsChecker.checkLength(values, 1);
 718   var n = TypeCaster.firstValueAsNumber(values[0]);
 719   if (n < 1) {
 720-    throw new CellError(ERRORS.NUM_ERROR, "Function LN parameter 1 value is " + n + ". It should be greater than 0.");
 721+    throw new NumError("Function LN parameter 1 value is " + n + ". It should be greater than 0.");
 722   }
 723   return Math.log(n);
 724 };
 725@@ -503,7 +502,7 @@ var CEILING = function (...values) : number {
 726   }
 727   var significance = TypeCaster.firstValueAsNumber(values[1]);
 728   if (significance === 0) {
 729-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Function CEILING parameter 2 cannot be zero.");
 730+    throw new DivZeroError("Function CEILING parameter 2 cannot be zero.");
 731   }
 732   var precision = -Math.floor(Math.log(significance) / Math.log(10));
 733   if (num >= 0) {
 734@@ -528,7 +527,7 @@ var FLOOR = function (...values) : number {
 735   }
 736   var significance = TypeCaster.firstValueAsNumber(values[1]);
 737   if (significance === 0) {
 738-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Function FLOOR parameter 2 cannot be zero.");
 739+    throw new DivZeroError("Function FLOOR parameter 2 cannot be zero.");
 740   }
 741   significance = significance ? Math.abs(significance) : 1;
 742   var precision = -Math.floor(Math.log(significance) / Math.log(10));
 743@@ -550,7 +549,7 @@ var IF = function (...values) : any {
 744   ArgsChecker.checkLength(values, 3);
 745   if (values[0] instanceof Array) {
 746     if (values[0].length === 0) {
 747-      throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 748+      throw new RefError("Reference does not exist.");
 749     }
 750     return IF(values[0][0], values[1], values[2]);
 751   } else if (values[0] === "") {
 752@@ -614,7 +613,7 @@ var COUNTIFS = function (...values) {
 753     var otherCriteriaEvaluationSuccessfulSoFar = true;
 754     for (var x = 0; x < values.length; x += 2) {
 755       if (values[x].length < values[0].length) {
 756-        throw new CellError(ERRORS.VALUE_ERROR, "Array arguments to COUNTIFS are of different size.");
 757+        throw new ValueError("Array arguments to COUNTIFS are of different size.");
 758       }
 759       var criteriaEvaluation = criteriaEvaluationFunctions[x+1];
 760       if (otherCriteriaEvaluationSuccessfulSoFar) {
 761@@ -732,7 +731,7 @@ var SUMSQ = function (...values) {
 762   for (var i = 0; i < values.length; i++) {
 763     if (values[i] instanceof Array) {
 764       if (values[i].length === 0) {
 765-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 766+        throw new RefError("Reference does not exist.");
 767       }
 768       result = result + SUMSQ.apply(this, Filter.filterOutNonNumberValues(values[i]));
 769     } else {
 770@@ -871,7 +870,7 @@ var SUMX2PY2 = function (...values) : number {
 771   var arrOne = Filter.flattenAndThrow(values[0]);
 772   var arrTwo = Filter.flattenAndThrow(values[1]);
 773   if (arrOne.length !== arrTwo.length) {
 774-    throw new CellError(ERRORS.NA_ERROR, "Array arguments to SUMX2PY2 are of different size.");
 775+    throw new NAError("Array arguments to SUMX2PY2 are of different size.");
 776   }
 777   var result = 0;
 778   for (var i = 0; i < arrOne.length; i++) {
 779@@ -897,7 +896,7 @@ var SUMX2MY2 = function (...values) : number {
 780   var arrOne = Filter.flattenAndThrow(values[0]);
 781   var arrTwo = Filter.flattenAndThrow(values[1]);
 782   if (arrOne.length !== arrTwo.length) {
 783-    throw new CellError(ERRORS.NA_ERROR, "Array arguments to SUMX2MY2 are of different size.");
 784+    throw new NAError("Array arguments to SUMX2MY2 are of different size.");
 785   }
 786   var result = 0;
 787   for (var i = 0; i < arrOne.length; i++) {
 788@@ -966,7 +965,7 @@ var SUMPRODUCT = function (...values) : number {
 789   for (var x = 1; x < values.length; x++) {
 790     flattenedValues.push(Filter.flattenAndThrow(values[x]));
 791     if (flattenedValues[x].length !== flattenedValues[0].length) {
 792-      throw new CellError(ERRORS.VALUE_ERROR, "SUMPRODUCT has mismatched range sizes. Expected count: "
 793+      throw new ValueError("SUMPRODUCT has mismatched range sizes. Expected count: "
 794         + flattenedValues[0].length + ". Actual count: " + flattenedValues[0].length + ".");
 795     }
 796   }
 797@@ -1008,14 +1007,14 @@ var COMBIN = function (...values) : number {
 798   var n = TypeCaster.firstValueAsNumber(values[0]);
 799   var c = TypeCaster.firstValueAsNumber(values[1]);
 800   if (n < c) {
 801-    throw new CellError(ERRORS.NUM_ERROR, "Function COMBIN parameter 2 value is "
 802+    throw new NumError("Function COMBIN parameter 2 value is "
 803       + c + ". It should be less than or equal to value of Function COMBIN parameter 1 with " + n + ".");
 804   }
 805   n = Math.floor(n);
 806   c = Math.floor(c);
 807   var div = fact(c) * fact(n - c);
 808   if (div === 0) {
 809-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function COMBIN caused a divide by zero error.");
 810+    throw new DivZeroError("Evaluation of function COMBIN caused a divide by zero error.");
 811   }
 812   return fact(n) / div;
 813 };
 814diff --git a/src/RawFormulas/Statistical.ts b/src/RawFormulas/Statistical.ts
 815index 742244f..e1aeb88 100644
 816--- a/src/RawFormulas/Statistical.ts
 817+++ b/src/RawFormulas/Statistical.ts
 818@@ -5,13 +5,12 @@ import {
 819   TypeCaster
 820 } from "./Utils";
 821 import {
 822-  CellError
 823+  RefError, NumError, DivZeroError, NAError
 824 } from "../Errors";
 825 import {
 826   SUM,
 827   ABS
 828 } from "./Math"
 829-import * as ERRORS from "../Errors";
 830 
 831 
 832 /**
 833@@ -49,7 +48,7 @@ var MEDIAN = function (...values) : number {
 834   values.forEach(function (currentValue) {
 835     if (currentValue instanceof Array) {
 836       if (currentValue.length === 0) {
 837-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 838+        throw new RefError("Reference does not exist.");
 839       }
 840       var filtered = Filter.filterOutStringValues(currentValue);
 841       sortedArray = sortedArray.concat(filtered);
 842@@ -66,7 +65,7 @@ var MEDIAN = function (...values) : number {
 843     return TypeCaster.valueToNumber(sortedArray[0]);
 844   }
 845   if (sortedArray.length === 0) {
 846-    throw new CellError(ERRORS.NUM_ERROR, "MEDIAN has no valid input data.");
 847+    throw new NumError("MEDIAN has no valid input data.");
 848   }
 849   // even number of values
 850   if (sortedArray.length % 2 === 0) {
 851@@ -95,7 +94,7 @@ var AVERAGE = function (...values) : number {
 852   for (var i = 0; i < values.length; i++) {
 853     if (values[i] instanceof Array) {
 854       if (values[i].length === 0) {
 855-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 856+        throw new RefError("Reference does not exist.");
 857       }
 858       var filtered = Filter.filterOutStringValues(values[i]);
 859       result = result + SUM.apply(this, filtered);
 860@@ -124,7 +123,7 @@ var AVEDEV = function (...values) {
 861     var X = values[i];
 862     if (X instanceof Array) {
 863       if (X.length === 0) {
 864-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 865+        throw new RefError("Reference does not exist.");
 866       }
 867       arrayValues.push(X);
 868     } else {
 869@@ -145,7 +144,7 @@ var AVEDEV = function (...values) {
 870     count++;
 871   }
 872   if (count === 0) {
 873-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function AVEDEV caused a devide by zero error.");
 874+    throw new DivZeroError("Evaluation of function AVEDEV caused a devide by zero error.");
 875   }
 876   var mean = result / count;
 877 
 878@@ -168,7 +167,7 @@ var AVERAGEA = function (...values) {
 879   for (var i = 0; i < values.length; i++) {
 880     if (values[i] instanceof Array) {
 881       if (values[i].length === 0) {
 882-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 883+        throw new RefError("Reference does not exist.");
 884       }
 885       var filtered = Filter.stringValuesToZeros(values[i]);
 886       result = result + SUM.apply(this, filtered);
 887@@ -179,7 +178,7 @@ var AVERAGEA = function (...values) {
 888     }
 889   }
 890   if (count === 0) {
 891-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function AVEDEV caused a devide by zero error.");
 892+    throw new DivZeroError("Evaluation of function AVEDEV caused a devide by zero error.");
 893   }
 894   return result / count;
 895 };
 896@@ -198,7 +197,7 @@ var CORREL = function (...values) : number {
 897   }
 898   function variance(arr, flag) {
 899     if ((arr.length - (flag ? 1 : 0)) === 0) {
 900-      throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function CORREL caused a divide by zero error.");
 901+      throw new DivZeroError("Evaluation of function CORREL caused a divide by zero error.");
 902     }
 903     return sumsqerr(arr) / (arr.length - (flag ? 1 : 0));
 904   }
 905@@ -212,7 +211,7 @@ var CORREL = function (...values) : number {
 906   }
 907   function mean(arr) {
 908     if (arr.length === 0) {
 909-      throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function CORREL caused a divide by zero error.");
 910+      throw new DivZeroError("Evaluation of function CORREL caused a divide by zero error.");
 911     }
 912     return sum(arr) / arr.length;
 913   }
 914@@ -236,7 +235,7 @@ var CORREL = function (...values) : number {
 915       sq_dev[i] = (arr1[i] - u) * (arr2[i] - v);
 916     }
 917     if ((arr1Len - 1) === 0) {
 918-      throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function CORREL caused a divide by zero error.");
 919+      throw new DivZeroError("Evaluation of function CORREL caused a divide by zero error.");
 920     }
 921     return sum(sq_dev) / (arr1Len - 1);
 922   }
 923@@ -248,14 +247,14 @@ var CORREL = function (...values) : number {
 924     values[1] = [values[1]];
 925   }
 926   if (values[0].length !== values[1].length) {
 927-    throw new CellError(ERRORS.NA_ERROR, "CORREL has mismatched argument count " + values[0] + " vs " + values[1] + ".");
 928+    throw new NAError("CORREL has mismatched argument count " + values[0] + " vs " + values[1] + ".");
 929   }
 930   var arr1 = Filter.filterOutNonNumberValues(Filter.flattenAndThrow(values[0]));
 931   var arr2 = Filter.filterOutNonNumberValues(Filter.flattenAndThrow(values[1]));
 932   var stdevArr1 = stdev(arr1, 1);
 933   var stdevArr2 = stdev(arr2, 1);
 934   if (stdevArr1 === 0 || stdevArr2 === 0) {
 935-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function CORREL caused a divide by zero error.");
 936+    throw new DivZeroError("Evaluation of function CORREL caused a divide by zero error.");
 937   }
 938   return covariance(arr1, arr2) / stdevArr1 / stdevArr2;
 939 };
 940@@ -453,7 +452,7 @@ var FDIST$LEFTTAILED = function (...values) : number|undefined|boolean {
 941   ArgsChecker.checkLength(values, 4);
 942   var x = TypeCaster.firstValueAsNumber(values[0]);
 943   if (x < 0) {
 944-    throw new CellError(ERRORS.NUM_ERROR, "Function F.DIST parameter 1 value is " + x + ". It should be greater than or equal to 0.");
 945+    throw new NumError("Function F.DIST parameter 1 value is " + x + ". It should be greater than or equal to 0.");
 946   }
 947   var d1 = TypeCaster.firstValueAsNumber(values[1]);
 948   var d2 = TypeCaster.firstValueAsNumber(values[2]);
 949@@ -619,7 +618,7 @@ var FISHER = function (...values) : number {
 950   ArgsChecker.checkLength(values, 1);
 951   var x = TypeCaster.firstValueAsNumber(values[0]);
 952   if (x <= -1 || x >= 1) {
 953-    throw new CellError(ERRORS.NUM_ERROR, "Function FISHER parameter 1 value is " + x + ". Valid values are between -1 and 1 exclusive.");
 954+    throw new NumError("Function FISHER parameter 1 value is " + x + ". Valid values are between -1 and 1 exclusive.");
 955   }
 956   return Math.log((1 + x) / (1 - x)) / 2;
 957 };
 958@@ -649,7 +648,7 @@ var MAX = function (...values) {
 959   for (var i = 0; i < values.length; i++) {
 960     if (values[i] instanceof Array) {
 961       if (values[i].length === 0) {
 962-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 963+        throw new RefError("Reference does not exist.");
 964       }
 965       var filtered = Filter.filterOutStringValues(values[i]);
 966       if (filtered.length !== 0) {
 967@@ -685,7 +684,7 @@ var MIN = function (...values) {
 968   for (var i = 0; i < values.length; i++) {
 969     if (values[i] instanceof Array) {
 970       if (values[i].length === 0) {
 971-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
 972+        throw new RefError("Reference does not exist.");
 973       }
 974       var filtered = Filter.filterOutStringValues(values[i]);
 975       if (filtered.length !== 0) {
 976@@ -736,7 +735,7 @@ var AVERAGEIF = function (...values) {
 977     }
 978   }
 979   if (count === 0) {
 980-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function AVERAGEIF caused a divide by zero error.");
 981+    throw new DivZeroError("Evaluation of function AVERAGEIF caused a divide by zero error.");
 982   }
 983   return result / count;
 984 };
 985diff --git a/src/RawFormulas/Text.ts b/src/RawFormulas/Text.ts
 986index e18bd8a..999ffe4 100644
 987--- a/src/RawFormulas/Text.ts
 988+++ b/src/RawFormulas/Text.ts
 989@@ -3,9 +3,8 @@ import {
 990   TypeCaster
 991 } from "./Utils";
 992 import {
 993-  CellError
 994+  ValueError, NumError, RefError
 995 } from "../Errors";
 996-import * as ERRORS from "../Errors";
 997 
 998 /**
 999  * Computes the value of a Roman numeral.
1000@@ -16,7 +15,7 @@ import * as ERRORS from "../Errors";
1001 var ARABIC = function (text?) {
1002   ArgsChecker.checkLength(arguments, 1);
1003   if (typeof text !== "string") {
1004-    throw new CellError(ERRORS.VALUE_ERROR, 'Invalid roman numeral in ARABIC evaluation.');
1005+    throw new ValueError('Invalid roman numeral in ARABIC evaluation.');
1006   }
1007   var negative = false;
1008   if (text[0] === "-") {
1009@@ -25,7 +24,7 @@ var ARABIC = function (text?) {
1010   }
1011   // Credits: Rafa? Kukawski
1012   if (!/^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/.test(text)) {
1013-    throw new CellError(ERRORS.VALUE_ERROR, 'Invalid roman numeral in ARABIC evaluation.');
1014+    throw new ValueError('Invalid roman numeral in ARABIC evaluation.');
1015   }
1016   var r = 0;
1017   text.replace(/[MDLV]|C[MD]?|X[CL]?|I[XV]?/g, function (i) {
1018@@ -47,7 +46,7 @@ var CHAR = function (...values) : string {
1019   ArgsChecker.checkLength(values, 1);
1020   var n = TypeCaster.firstValueAsNumber(values[0]);
1021   if (n < 1 || n > 1114112) { //limit
1022-    throw new CellError(ERRORS.NUM_ERROR, "Function CHAR parameter 1 value " + n + " is out of range.");
1023+    throw new NumError("Function CHAR parameter 1 value " + n + " is out of range.");
1024   }
1025   return String.fromCharCode(n);
1026 };
1027@@ -62,7 +61,7 @@ var CODE = function (...values) : number {
1028   ArgsChecker.checkLength(values, 1);
1029   var text = TypeCaster.firstValueAsString(values[0]);
1030   if (text === "") {
1031-    throw new CellError(ERRORS.VALUE_ERROR, "Function CODE parameter 1 value should be non-empty.");
1032+    throw new ValueError("Function CODE parameter 1 value should be non-empty.");
1033   }
1034   return text.charCodeAt(0);
1035 };
1036@@ -115,7 +114,7 @@ var CONCATENATE = function (...values) : string {
1037   for (var i = 0; i < values.length; i++) {
1038     if (values[i] instanceof Array) {
1039       if (values[i].length === 0) {
1040-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1041+        throw new RefError("Reference does not exist.");
1042       }
1043       string += CONCATENATE.apply(this, arguments[i]);
1044     } else {
1045diff --git a/src/RawFormulas/Utils.ts b/src/RawFormulas/Utils.ts
1046index 51712a4..2be95f1 100644
1047--- a/src/RawFormulas/Utils.ts
1048+++ b/src/RawFormulas/Utils.ts
1049@@ -1,8 +1,7 @@
1050 /// <reference path="../../node_modules/moment/moment.d.ts"/>
1051 import * as moment from "moment";
1052-import {CellError, VALUE_ERROR} from "../Errors"
1053-import * as ERRORS from "../Errors"
1054-import {ExcelDate} from "../ExcelDate";
1055+import { ValueError, RefError, NAError, DivZeroError } from "../Errors"
1056+import { ExcelDate } from "../ExcelDate";
1057 
1058 /**
1059  * Converts wild-card style expressions (in which * matches zero or more characters, and ? matches exactly one character)
1060@@ -576,7 +575,7 @@ class TypeCaster {
1061       }
1062     }
1063     if (m === undefined || !m.isValid()) {
1064-      throw new CellError(VALUE_ERROR, "DATEVALUE parameter '" + dateString + "' cannot be parsed to date/time.");
1065+      throw new ValueError("DATEVALUE parameter '" + dateString + "' cannot be parsed to date/time.");
1066     }
1067     return new ExcelDate(m);
1068   }
1069@@ -596,13 +595,13 @@ class TypeCaster {
1070       if (value.indexOf(".") > -1) {
1071         var fl = parseFloat(value.replace("$", ""));
1072         if (isNaN(fl)) {
1073-          throw new CellError(ERRORS.VALUE_ERROR, "Function ____ expects number values, but is text and cannot be coerced to a number.");
1074+          throw new ValueError("Function ____ expects number values, but is text and cannot be coerced to a number.");
1075         }
1076         return fl;
1077       }
1078       var fl = parseInt(value.replace("$", ""));
1079       if (isNaN(fl)) {
1080-        throw new CellError(ERRORS.VALUE_ERROR, "Function ____ expects number values, but is text and cannot be coerced to a number.");
1081+        throw new ValueError("Function ____ expects number values, but is text and cannot be coerced to a number.");
1082       }
1083       return fl;
1084     } else if (typeof value === "boolean") {
1085@@ -633,7 +632,7 @@ class TypeCaster {
1086     if (typeof value === "number") {
1087       return value !== 0;
1088     } else if (typeof value === "string") {
1089-      throw new CellError(ERRORS.VALUE_ERROR, "___ expects boolean values. But '" + value + "' is a text and cannot be coerced to a boolean.")
1090+      throw new ValueError("___ expects boolean values. But '" + value + "' is a text and cannot be coerced to a boolean.")
1091     } else if (typeof value === "boolean") {
1092       return value;
1093     }
1094@@ -683,7 +682,7 @@ class TypeCaster {
1095   static firstValueAsNumber(input: any) : number {
1096     if (input instanceof Array) {
1097       if (input.length === 0) {
1098-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1099+        throw new RefError("Reference does not exist.");
1100       }
1101       return TypeCaster.firstValueAsNumber(input[0]);
1102     }
1103@@ -698,7 +697,7 @@ class TypeCaster {
1104   static firstValueAsString(input: any) : string {
1105     if (input instanceof Array) {
1106       if (input.length === 0) {
1107-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1108+        throw new RefError("Reference does not exist.");
1109       }
1110       return TypeCaster.firstValueAsString(input[0]);
1111     }
1112@@ -708,7 +707,7 @@ class TypeCaster {
1113   static firstValue(input: any) : any {
1114     if (input instanceof Array) {
1115       if (input.length === 0) {
1116-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1117+        throw new RefError("Reference does not exist.");
1118       }
1119       return TypeCaster.firstValue(input[0]);
1120     }
1121@@ -723,7 +722,7 @@ class TypeCaster {
1122   static firstValueAsBoolean(input: any): boolean {
1123     if (input instanceof Array) {
1124       if (input.length === 0) {
1125-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1126+        throw new RefError("Reference does not exist.");
1127       }
1128       return TypeCaster.firstValueAsBoolean(input[0]);
1129     }
1130@@ -738,7 +737,7 @@ class TypeCaster {
1131   static firstValueAsExcelDate(input: any) : ExcelDate {
1132     if (input instanceof Array) {
1133       if (input.length === 0) {
1134-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1135+        throw new RefError("Reference does not exist.");
1136       }
1137       return TypeCaster.firstValueAsExcelDate(input[0]);
1138     }
1139@@ -762,10 +761,10 @@ class TypeCaster {
1140         if (TypeCaster.canCoerceToNumber(value)) {
1141           return new ExcelDate(TypeCaster.valueToNumber(value));
1142         }
1143-        throw new CellError(ERRORS.VALUE_ERROR, "___ expects date values. But '" + value + "' is a text and cannot be coerced to a date.")
1144+        throw new ValueError("___ expects date values. But '" + value + "' is a text and cannot be coerced to a date.")
1145       }
1146     } else if (typeof value === "boolean") {
1147-      throw new CellError(ERRORS.VALUE_ERROR, "___ expects date values. But '" + value + "' is a text and cannot be coerced to a date.")
1148+      throw new ValueError("___ expects date values. But '" + value + "' is a text and cannot be coerced to a date.")
1149     }
1150   }
1151 }
1152@@ -810,7 +809,7 @@ class Filter {
1153   static flattenAndThrow(values: Array<any>) : Array<any> {
1154     return values.reduce(function (flat, toFlatten) {
1155       if (Array.isArray(toFlatten) && toFlatten.length === 0) {
1156-        throw new CellError(ERRORS.REF_ERROR, "Reference does not exist.");
1157+        throw new RefError("Reference does not exist.");
1158       }
1159       return flat.concat(Array.isArray(toFlatten) ? Filter.flattenAndThrow(toFlatten) : toFlatten);
1160     }, []);
1161@@ -858,7 +857,7 @@ class ArgsChecker {
1162    */
1163   static checkLength(args: any, length: number) {
1164     if (args.length !== length) {
1165-      throw new CellError(ERRORS.NA_ERROR, "Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
1166+      throw new NAError("Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
1167     }
1168   }
1169 
1170@@ -869,7 +868,7 @@ class ArgsChecker {
1171    */
1172   static checkAtLeastLength(args: any, length: number) {
1173     if (args.length < length) {
1174-      throw new CellError(ERRORS.NA_ERROR, "Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
1175+      throw new NAError("Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
1176     }
1177   }
1178 
1179@@ -881,7 +880,7 @@ class ArgsChecker {
1180    */
1181   static checkLengthWithin(args: any, low: number, high: number) {
1182     if (args.length > high || args.length < low) {
1183-      throw new CellError(ERRORS.NA_ERROR, "Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
1184+      throw new NAError("Wrong number of arguments to ___. Expected 1 arguments, but got " + args.length + " arguments.");
1185     }
1186   }
1187 }
1188@@ -904,7 +903,7 @@ class Serializer {
1189 var checkForDevideByZero = function(n : number) : number {
1190   n = +n;  // Coerce to number.
1191   if (!n) {  // Matches +0, -0, NaN
1192-    throw new CellError(ERRORS.DIV_ZERO_ERROR, "Evaluation of function caused a divide by zero error.");
1193+    throw new DivZeroError("Evaluation of function caused a divide by zero error.");
1194   }
1195   return n;
1196 };
1197diff --git a/tests/DateFormulasTest.ts b/tests/DateFormulasTest.ts
1198index be9d600..d1f5635 100644
1199--- a/tests/DateFormulasTest.ts
1200+++ b/tests/DateFormulasTest.ts
1201@@ -10,15 +10,14 @@ function catchAndAssertEquals(toExecute, expected) {
1202     toExecute();
1203     toThrow = true;
1204   } catch (actualError) {
1205-    if (actualError.message != expected) {
1206-      console.log(expected, "not equal to", actualError.message);
1207+    if (actualError.name != expected) {
1208+      console.log(expected, "not equal to", actualError.name);
1209     }
1210   }
1211   if (toThrow) {
1212     throw new Error("expected error: " + expected);
1213   }
1214 }
1215-
1216 // Test EDATE
1217 assertEquals(EDATE(DATE(1992, 6, 24), 1), DATE(1992, 7, 24));
1218 assertEquals(EDATE(DATE(1992, 5, 24), 2), DATE(1992, 7, 24));
1219diff --git a/tests/FormulasTest.ts b/tests/FormulasTest.ts
1220index 6c65bf3..620ea18 100644
1221--- a/tests/FormulasTest.ts
1222+++ b/tests/FormulasTest.ts
1223@@ -17,8 +17,8 @@ function catchAndAssertEquals(toExecute, expected) {
1224     toExecute();
1225     toThrow = true;
1226   } catch (actualError) {
1227-    if (actualError.message != expected) {
1228-      console.log(expected, "not equal to", actualError.message);
1229+    if (actualError.name != expected) {
1230+      console.log(expected, "not equal to", actualError.name);
1231     }
1232   }
1233   if (toThrow) {