The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
NAME
    Number::WithError - Numbers with error propagation and scientific
    rounding

SYNOPSIS
      use Number::WithError;
  
      my $num = Number::WithError->new(5.647, 0.31);
      print $num . "\n";
      # prints '5.65e+00 +/- 3.1e-01'
      # (I.e. it automatically does scientific rounding)
  
      my $another = $num * 3;
      print $another . "\n";
      # propagates the error assuming gaussian errors
      # prints '1.69e+01 +/- 9.3e-01'
  
      # trigonometric functions also work:
      print sin($another) . "\n";
      # prints '-9.4e-01 +/- 3.1e-01'
  
      my $third = $another ** $num;
      print $third. "\n";
      # propagates both errors into one.
      # prints '8.7e+06 +/- 8.1e+06'
  
      # shortcut for the constructor:
      use Number::WithError 'witherror';
      $num = witherror('0.00032678', ['2.5e-5', '3e-5'], 5e-6);
      # can deal with any number of errors, even with asymmetric errors
      print $num . "\n";
      # prints '3.268e-04 + 2.5e-05 - 3.00e-05 +/- 5.0e-06'
      # Note: It may be annyoing that they don't all have the same
      # exponent, but they *do* all have the sam significant digit!

DESCRIPTION
    This class is a container class for numbers with a number of associated
    symmetric and asymmetric errors. It overloads practically all common
    arithmetic operations and trigonometric functions to propagate the
    errors. It can do proper scientific rounding (as explained in more
    detail below in the documentation of the "significant_digit()" method).

    You can use Math::BigFloat objects as the internal representation of
    numbers in order to support arbitrary precision calculations.

    Errors are propagated using Gaussian error propagation.

    With a notable exception, the test suite covers way over ninety percent
    of the code. The remaining holes are mostly difficult-to-test corner
    cases and sanity tests. The comparison routines are the exception for
    which there will be more extensive tests in a future release.

OVERLOADED INTERFACE
    This module uses overload to enable the use of the ordinary Perl
    arithmetic operators on objects. All overloaded operations are also
    availlable via methods. Here is a list of overloaded operators and the
    equivalent methods. The assignment forms of arithmetic operators (e.g.
    "+=") are availlable if their normal counterpart is overloaded.

    Addition: "$x + $y" implemented by the "$x->add($y)" method.
    Increment: "$x++" implemented by the "$x->add(1)" method.
    Subtraction: "$x - $y" implemented by the "$x->subtract($y)" method
    Decrement: "$x--" implemented by the "$x->subtract(1)" method.
    Multiplication: "$x * $y" implemented by the "$x->multiply($y)" method.
    Division: "$x / $y" implemented by the "$x->divide($y)" method.
    Exponentiation: "$x ** $y" implemented by the "$x->exponentiate($y)"
    method.
    Sine: "sin($x)" implemented by the "$x->sin()" method.
    Cosine: "cos($x)" implemented by the "$x->cos()" method.
    Stringification "$x" is implemented by the "$x->round()" method.
    Cast to a number (i.e. numeric context) is implemented by the
    "$x->number()" method.
    Boolean context is implemented by the "$x->number()" method.
    Unary minus "-$x" is implemented by the "$x->multiply(-1)" method.
    Logical not is implemented via a boolean context.
    Absolute value "abs($x)" is implemented via "$x->abs()".
    Natural logarithm "log($x)" is implemented via "$x->log()".
    Square Root "sqrt($x)" is implemented via "$x->sqrt()".
    Numeric comparison operators "$x == $y", "$x != $y", etc. are
    implemented via "$x-$<gt"numeric_cmp($y)>.
    String comparison operators "$x eq $y", "$x ne $y", etc. are implemented
    via "$x-$<gt"full_cmp($y)>. They might not do what you expect. Please
    read the documentation.

    Here's a list of overloadable operations that aren't overloaded in the
    context of this module:

      << >> x . & ^ | atan2 int

CONSTRUCTORS
    All constructors accept Math::BigFloat objects in place of numbers.

  new
    This is the basic constructor for "Number::WithError" objects.

    New objects can be created in one of two ways:

    * The first argument is expected to be the number itself. Then come zero
      or more errors. Errors can either be a number, a reference to an array
      of two numbers, or "undef". In the former case, the number is treated
      as an uncertainty which has the same magnitude in both directions.
      (I.e. "+/- x") In case of an array reference, the first number is
      treated as the upper error boundary and the second as the lower
      boundary. (I.e. "+x, -y") "undef" is treated as zero error.

    * The second way to create objects is passing a single string to the
      constructor which is efficiently parsed into a number and any number
      of errors. I'll explain the format with an example:

        133.14e-5 +/- .1e-4 + 0.00002 - 1.0e-5 +/- .2e-4

      In this example, the first number is parsed as the actual number. The
      following number is treated as a symmetric error (.1e-4) The two
      following numbers are treated as the upper and lower boundary for a
      single error. Then comes another ordinary error. It is also legal to
      define the lower boundary of an error before the upper boundary. (I.e.
      "-1.0e-5 +0.00002")

      Whitespace is insignificant.

      For the sake of completeness, I'll mention the regular expression that
      is used to match numbers. It's taken from the official Perl FAQ to
      match floating point numbers:

        [+-]?(?=\d|\.\d)\d*(?:\.\d*)?(?:[Ee][+-]?\d+)?

      Don't worry if you don't understand it. Just run a few tests to see if
      it'll accept your numbers. Or go read "perldoc -q float" or pick up a
      book on C and read up on how they define floating point numbers.

    Note that trailing zeros currently have no effect. (Yes, this is a BUG!)

    The constructor returns a new object of this class or undef if something
    went wrong.

  new_big
    This is an alternative constructor for "Number::WithError" objects. It
    works exactly like "new" except that it makes all internal numbers
    instances of "Math::BigFloat" for high precision calculations.

    The module does not load "Math::BigFloat" at compile time to avoid
    loading a big module that isn't needed all the time. Instead, this
    module makes use of the prefork pragma and loads "Math::BigFloat" when
    needed at run-time.

  witherror
    This constructor is not a method. It is a subroutine that can be
    exported to your namespace on demand. It works exactly as the "new()"
    method except it's a subroutine and shorter.

    I'm normally not for this kind of shortcut in object-oriented code, but
    if you have to create a large number of "Number::WithError" numbers,
    you'll appreciate it. Trust me.

    Note to authors of subclasses: If you inherit from this module, you'll
    need to implement your own "witherror()" because otherwise, it will
    still return objects of this class, not your subclass.

  witherror_big
    This is also not a method. It does the same as "witherror()". It can
    also be optionally be exported to your namespace.

    It uses the "new_big" constructor instead of the "new" constructor used
    by "witherror()".

ARITHMETIC METHODS
    All of these methods implement an arithmetic operation on the object and
    the method's first parameter.

    The methods aren't mutators. That means they don't modify the object
    itself, but return the result of the operation as a new object.

    All of the methods accept either a plain number, a "Number::WithError"
    object or anything that is understood by the constructors as argument,

    All errors are correctly propagated using Gaussian Error Propagation.
    The formulae used for this are mentioned in the individual methods'
    documentation.

  add
    Adds the object a and the argument b. Returns a new object c.

    Formula: "c = a + b"

    Error Propagation: "err_c = sqrt( err_a^2 + err_b^2 )"

  subtract
    Subtracts the argument b from the object a. Returns a new object c.

    Formula: "c = a - b"

    Error Propagation: "err_c = sqrt( err_a^2 + err_b^2 )"

  multiply
    Multiplies the object a and the argument b. Returns a new object c.

    Formula: "c = a * b"

    Error Propagation: "err_c = sqrt( b^2 * err_a^2 + a^2 * err_b^2 )"

  divide
    Divides the object a by the argument b. Returns a new object c.

    Formula: "c = a / b"

    Error Propagation: "err-c = sqrt( err_a^2 / b^2 + a^2 * err_b^2 / b^4 )"

  exponentiate
    Raises the object a to the power of the argument b. Returns a new object
    c. Returns "undef" if a is negative because the error cannot be
    propagated in that case. (Can't take log of a negative value.)

    Also, please have a look at the error propagation formula below.
    Exponentiation and logarithms are operations that can become numerically
    unstable very easily.

    Formula: "c = a ^ b"

    Error Propagation: "err-c = sqrt( b^2 * a^(b-1) * err_a^2 + ln(a)^2 *
    a^b * err_b^2 )"

METHODS FOR BUILTIN FUNCTIONS
    These methods calculate functions of the object and return the result as
    a new object.

  sqrt
    Calculates the square root of the object a and returns the result as a
    new object c. Returns undef if a is negative.

    Formula: "c = sqrt(a)"

    Error Propagation: "err-c = sqrt( err-a^2 / (2*sqrt(a))^2 ) = abs( err-a
    / (2*sqrt(a)) )"

  log
    Calculates the natural logarithm of an object a. Returns a new object c.
    If a is negative, the function returns undef.

    Formula: "c = log(a)"

    Error Propagation: "err-c = sqrt( err-a^2 / a^2 ) = abs( err-a / a )"

  sin
    Calculates the sine of the object a and returns the result as a new
    object c.

    Formula: "c = sin(a)"

    Error Propagation: "err-c = sqrt( cos(a)^2 * err-a^2 ) = abs( cos(a) *
    err-a )"

  cos
    Calculates the cosine of the object a and returns the result as a new
    object c.

    Formula: "c = cos(a)"

    Error Propagation: "err-c = sqrt( sin(a)^2 * err-a^2 ) = abs( sin(a) *
    err-a )"

  tan
    Calculates the tangent of the object a and returns the result as a new
    object c.

    Formula: "c = tan(a)"

    Error Propagation: "err-c = sqrt( err-a^2 / cos(a)^4 ) = abs( err-a /
    cos(a)^2 )"

    Since there is no built-in "tan()" function, this operation is not
    available via the overloaded interface.

  abs
    Calculates the absolute value of an object a. Leaves the errors
    untouched. Returns a new object c.

    Formula: "c = abs(a)"

    Error Propagation: "err-c = err-a"

ROUNDING, STRINGIFICATION AND OUTPUT METHODS
    This section documents methods dealing with the extraction of data from
    the object. The methods implement rounding of numbers, stringification
    of the object and extracting meta information like the significant
    digit.

  number
    Determines the significant digit using the "significant_digit()" method,
    rounds the number that the object "number()" is called on represents to
    that digit and returns the rounded number.

    Regardless of the internal representation of the number, this returns an
    unblessed string / an unblessed floating point number.

    To gain access to the raw number representation in the object, use the
    "raw_number" method.

    Either way, the number will be in scientific notation. That means the
    first non-zero digit comes before the decimal point and following the
    decimal point and any number of digits is an exponent in "eXXX"
    notation.

  raw_number
    This method returns the internal representation of the number in the
    object. It does not round as appropriate. It does not clone
    "Math::BigFloat" objects either. So make sure you do that if necessary!

  round
    This method determines the significant digit using the
    "significant_digit()" method. Then, it rounds the number represented by
    the object and all associated errors to that digit.

    Then, the method concatenates the number with its errors and returns the
    resulting string. In case of symmetric errors, the string "+/-" will be
    prepended to the error. In case of asymmetric errors, a "+" will be
    prepended to the first/upper error component and a "-" to the
    second/lower error component.

    Returns the previously described string.

  significant_digit
    This method returns the significant digit of the number it is called on
    as an integer. If the number has no errors or all errors are "undef" or
    zero, this method returns "undef".

    The return value of this method is to be interpreted as follows: If this
    method returns -5, the significant digit is "1 * 10**-5" or 0.00001. If
    it returns 3, the significant digit is "1 * 10**3" or 1000. If it
    returns 0, the significant digit is 1.

    The return value is computed by the following algorithm: The individual
    significant digit of a single error is: Take the exponent of the first
    non-zero digit in the error. The digit after this first non-zero digit
    is the significant one.

    This method returns the minimum of the individual significant digits of
    all errors.

    That means:

      5 +/- 0.0132 + 0.5 - 1

    Will yield a return value of -3 since the first error has the lowest
    significant digit.

    This algorithm is also used for determining the significant digit for
    rounding. It is extremely important that you realize this isn't carved
    in stone. The way the significant digit is computed in the presence of
    errors is merely a convention. In this case, it stems from particle
    physics. It might well be that in your particular scientific community,
    there are other conventions. One, for example, is to use the second
    non-zero digit only if the first is a 1.

  error
    This method returns a reference to an array of errors of the object it
    is called on.

    Unlike the "raw_error()" method, this method takes proper care to copy
    all objects and references to defy action at a distance. The structure
    of the returned array reference is akin to that returned by
    "raw_error()".

    Furthermore, this method rounds all errors to the significant digit as
    determined by "significant_digit()".

  raw_error
    Returns the internal representation of the errors of the current object.
    Note that (just like "raw_number()", this does not clone the data for
    safe use without action at a distance. Instead, it directly returns the
    internal reference to the error structure. The structure is an array of
    errors. Each error may either be a string or floating point number or a
    "Math::BigFloat" object or an array reference. In case of an array
    reference, it is an asymmetric error. The inner array contains two
    strings/numbers/"Math::BigFloat"s.

    Note that this practically breaks encapsulation and code relying on it
    might break with future releases.

  as_array
    This method returns the information stored in the object as an array
    (i.e. a list in this context) which can be passed to the "new()" method
    to recreate the object.

    The first element of the return list will be the number itself. If the
    object uses "Math::BigFloat" for the internal representation, this
    element will be a copy of the internal object. Otherwise, it will be the
    internal representation of the number with full precision.

    Following the number will be all errors either as numbers,
    "Math::BigFloat" objects or arrays containing two asymmetric errors.
    (Either as numbers or objects as explained above.) The data returned by
    this method will be copied deeply before being returned.

  round_a_number
    This is a helper function which can round a number to the specified
    significant digit (defined as the return value of the
    "significant_digit" method):

      my $rounded = round_a_number(12.01234567, -3);
      # $rounded is now 1.2012e01

COMPARISON
    This section lists methods that implement different comparisons between
    objects.

  numeric_cmp
    This method implements a numeric comparison of two numbers. It compares
    the object it is called on to the first argument of the method. If the
    first argument is omitted or undefined, the method returns "undef".

    *Numeric comparison* means in this case that the represented numbers
    will be rounded and then compared. If you would like a comparison that
    takes care of errors, please have a look at the "full_cmp()" method.

    The method returns -1 if the rounded number represented by the object is
    numerically less than the rounded number represented by the first
    argument. It returns 0 if they are equal and 1 if the object's rounded
    number is more than that of the argument.

    This method implements the overloaded numeric comparison operations.

  full_cmp
    This method implements a full comparison of two objects. That means, it
    takes their numeric values, rounds them and compares them just like the
    "numeric_cmp()" method.

    If, however, the numbers are equal, this method iterates over the
    errors, rounds them and then compares them. If all errors are equal,
    this method returns 0. If an error is found to differ, the method
    returns 1 in case the object's error is larger and -1 in case the
    argument's error is larger.

    Comparing an asymmetric error to a symmetric error is a special case. It
    can never be the same error, hence the method will not return 0.
    Instead, it guesses which error is larger by using the upper error bound
    of the asymmetric error. (Well, yes, not very useful.)

SUPPORT
    Bugs should be reported via the CPAN bug tracker at

    <http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Number-WithError>

    For other issues, contact the author.

SEE ALSO
    You may use Math::BigFloat with this module. Also, it should be possible
    to use Math::Symbolic to calculate larger formulas. Just assign a
    "Number::WithError" object to the "Math::Symbolic" variables and it
    should work.

    You also possibly want to have a look at the prefork pragma.

    The test suite is implemented using the Test::LectroTest module. In
    order to keep the total test time in reasonable bounds, the default
    number of test attempts to falsify the test properties is kept at a low
    number of 100. You can enable more rigorous testing by setting the
    environment variable "PERL_TEST_ATTEMPTS" to a higher value. A value in
    the range of 1500 to 3000 is probably a good idea, but takes a long time
    to test.

AUTHOR
    Steffen Mueller <modules at steffen-mueller dot net>,
    <http://steffen-mueller.net/>

COPYRIGHT
    Copyright 2006-2010 Steffen Mueller.

    This program is free software; you can redistribute it and/or modify it
    under the same terms as Perl itself.