Number-WithError

 view release on metacpan or  search on metacpan

lib/Number/WithError.pm  view on Meta::CPAN

=item *

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 C<undef>. In the former case, the number
is treated as an uncertainty which has the same magnitude in both
directions. (I.e. C<+/- 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. C<+x, -y>) C<undef> is treated as zero error.

=item *

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 (C<.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. C<-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 C<perldoc -q float>
or pick up a book on C and read up on how they define floating
point numbers.

=back

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

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

=cut

sub new {
  my $proto = shift;
  my $class = ref($proto)||$proto;

  # clone
  if (ref($proto) and not @_) {
    my $num = $proto->{num};
    $num = $num->copy() if ref($num);
    my $err =  [];
    foreach (@{$proto->{errors}}) {
      push @$err, ref($_) eq 'ARRAY' ? [map {ref($_) ? $_->copy() : $_} @$_] : (ref($_) ? $_->copy() : $_)
    }
    return bless {num => $num, errors => $err} => $class;
  }

  return undef if not @_;

  my $num = shift;
  return undef if not defined $num;

  if (not @_) {
    return _parse_string($num, $class);
  }

  my $errors = [];
  my $self = {
    num => $num,
    errors => $errors,
  };
  bless $self => $class;


  while (@_) {
    my $err = shift;
    if (_ARRAY($err)) {
      if (@$err == 1) {
        push @$errors, CORE::abs($err->[0] || 0);
      }
      else {
        push @$errors, [CORE::abs($err->[0] || 0), CORE::abs($err->[1] || 0)];
      }
    }
    else {
      push @$errors, CORE::abs($err || 0);
    }
  }

  return $self;
}


# This parses a string into an object
sub _parse_string {
  my $str = shift;
  my $class = shift;

  return undef unless $str =~ /\G\s*($CFloat)/cgo;
  my $num = $1;
  my $err = [];
  while (1) {
    if ($str =~ /\G \s* \+ \s* \/ \s* \- \s* ($CFloat)/cgxo) {
      push @$err, CORE::abs($1);
    }
    elsif ($str =~ / \G \s* \+ \s* ($CFloat) \s* \- \s* ($CFloat)/cgxo) {
      push @$err,  [CORE::abs($1), CORE::abs($2)];
    }
    elsif ($str =~ /\G \s* \- \s* ($CFloat) \s* \+ \s* ($CFloat)/cgxo) {
      push @$err,  [CORE::abs($2), CORE::abs($1)];
    }
    else {
      last;
    }
  }
  return bless { num => $num, errors => $err } => $class;
}

=head2 new_big

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

The module does not load C<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 L<prefork> pragma and loads C<Math::BigFloat>
when needed at run-time.

=cut

sub new_big {
  my $obj = shift()->new(@_);

  return undef if not defined $obj;

  require Math::BigFloat;
  $obj->{num} = Math::BigFloat->new($obj->{num});

  foreach my $e (@{$obj->{errors}}) {
    if (_ARRAY0($e)) {
      @$e = map { Math::BigFloat->new($_) } @$e;
    }
    else {
      $e = Math::BigFloat->new($e);
    }
  }
  return $obj;
}


=head2 witherror

This constructor is B<not> a method. It is a subroutine that
can be exported to your namespace on demand. It works exactly
as the C<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
C<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 C<witherror()> because otherwise,
it will still return objects of this class, not your subclass.

=cut

sub witherror {  Number::WithError->new(@_) }


=head2 witherror_big

This is also B<not> a method. It does the same as C<witherror()>.
It can also be optionally be exported to your namespace.

 view all matches for this distribution
 view release on metacpan -  search on metacpan

( run in 0.821 second using v1.00-cache-2.02-grep-82fe00e-cpan-1925d2aa809 )