Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Rounding Errors
by mattr (Curate) on Jan 07, 2001 at 17:26 UTC | |
Here's what I did when you asked that question: Look up http://search.cpan.org Search for Math::BigFloat Click on "Math::BigFloat" as it appears under Perl5.005_03 Realize it must already be installed on my system! So at command line I typed man "Math::BigFloat" for the manual. You probably got this far, but it is a bit terse (read "cryptic"). If you don't have a Unix command line in front of you, check out the manual page which shows up at the above link: http://search.cpan.org/doc/GBARR/perl5.005_03/lib/Math/BigFloat.pm There is a little longer manual page for the version of the library provided with perl 5.7, which I don't have but here's the link (it showed up in the search results above): http://search.cpan.org/doc/JHI/perl-5.7.0/lib/Math/BigFloat.pm This library is object oriented, meaning that it lets you create an object which represents a long floating point number, and then perform operations on such objects. It also transforms ("overloads") common arithmetic operators so that you can have fun and appear to multiply and divide really huge number objects together by just typing a multiplication or division sign. Overloading is quick and fun but it is sometimes hard to debug, and may not give you access to subtle points in functionality of the library. The Synopsis section shows the "use" line you need to load the library, and a sample line which shows you how to create a bigfloat object. By picking a variable name and setting it to the output of the library's "new" command you can generate a new bigfloat object based on a long floating point number which you provide as a string, in quotes. You could append a number of strings together to make a longer one if you like. Since Perl uses the same type (scalar) variable for strings and numbers, you can mix them together. Either of these following two ways of stating it would create a bigfloat object (technically, an "instance" of the object) for you: $alongstring = "143208.13420789709814309781432"; # just some random keystrokes $hisfloat = Math::BigFloat->new($alongstring); $herfloat = new Math::BigFloat($alongstring); You can also leave out the parentheses, as the documentation does. The "new" method (think subroutine) is special in that you can type it in advance so it sounds like English. But normally an arrow is used when you want to access a method which belongs to a certain instance of an object. Since $hisfloat and $herfloat inherit the methods defined in the library, such as "fmul" and "fdiv". Here is the same thing first calling methods, then using the overloading provided by the library. I guessed that NSTR in the documentation means "any number string or a bigfloat object". Usually overloading just works with objects, but it is even looser than that in this library (which can be dangerous, see below). print $hisfloat->fmul($herfloat); print $hisfloat * $herfloat; Here is a test you can do on the command line (all on one line): perl -e 'use Math::BigFloat; $s="14372098.1320498713209847102398471"; $f = new Math::BigFloat($s); $g = Math::BigFloat->new("13409870123087.1329481723049817230498712304981"); print $f * $g * 3.14159 , "\n";' 605472261221004971587.2079631921116943252504708191763655250636246607274730865378109 The library seems to add engineering notation when you call by method instead of using overloaded arithmetic operators (no loss of precision): perl -e 'use Math::BigFloat; $s="14372098.1320498713209847102398471"; $f = new Math::BigFloat($s); $g = Math::BigFloat->new("13409870123087.1329481723049817230498712304981"); print $f->fmul($g * 3.14159) , "\n";' +6054722612210049715872079631921116943252504708191763655250636246607274730865378109E-61 But truncates it to normal short floating point length, when you put a bare number outside the parentheses: perl -e 'use Math::BigFloat; $s="14372098.1320498713209847102398471"; $f = new Math::BigFloat($s); $g = Math::BigFloat->new("13409870123087.1329481723049817230498712304981"); print $f->fmul($g) * 3.14159 , "\n";' 6.05472261221005e+20 Which I fixed by keeping everything a BigFloat object: perl -e 'use Math::BigFloat; $s="14372098.1320498713209847102398471"; $f = new Math::BigFloat($s); $g = Math::BigFloat->new("13409870123087.1329481723049817230498712304981"); print $f->fmul($g) * (new Math::BigFloat("3.14159")) , "\n";' 605472261221004971587.2079631921116943252504708191763655250636246607274730865378109 I can't tell if this is flakiness or proper functioning, since the docs do say that it is a development version I am using. I recommend that you do everything in a program, one operation per line, and only explicit method calls, not operator overloading, to ensure debuggable, consistent results. You can also look at the module code itself for hints as to what is going on.. try "locate BigFloat.pm" on your system. You will note that unless you explicitly specify scale, $div_scale is 40 in the code which means you get only 40 digits of accuracy in that case. Here is an example of the difference which global settings for your library will have. Instead of div_scale (which can be set on a per-instance basis anyway) I am looking at trunc: perl -e 'use Math::BigFloat; $s="14372098.1320498713209847102398471"; $f = new Math::BigFloat($s); $g = Math::BigFloat->new("13409870123087.1329481723049817230498712304981"); print $f->fdiv($g) , "\nmode: " +107175520718176952100999428361427933683486722E-50 mode: even perl -e 'use Math::BigFloat; $Math::BigFloat::rnd_mode = "trunc"; $s="14372098.1320498713209847102398471"; $f = new Math::BigFloat($s); $g = Math::BigFloat->new("13409870123087.1329481723049817230498712304981"); print $f->fdiv($g) , "\nmode: ", $Math::BigFloat::rnd_mode, "\n";' +1071755207181769521009994283614279336834867219E-51 mode: trunc Well that was fun, back to work for me.. Hope this helps. Matt | [reply] |
by Anonymous Monk on Jan 11, 2001 at 05:32 UTC | |
| [reply] |
|
Re: Rounding Errors
by Anonymous Monk on Jan 07, 2001 at 01:26 UTC | |
| [reply] [d/l] |
|
Re: Rounding Errors
by blueAdept (Beadle) on Jan 08, 2001 at 11:06 UTC | |
Floating point numbers need to be represented in binary, but not just the number itself. Things like whether they're positive or negative, and their power(decimal position). Standardized schemes are used internally for doing this, but each time the computer encounters a floating point number it needs to digest/squish it down into the special binary format. So one floating point number(i.e. 1.234) is represented with a few binary numbers(values) - the number, its power, and sign. Its during this conversion process that things get truncated. Depending upon the bit width of the FPU(as you mentioned), the conversion process will differ in the amount of truncation that needs to be done. I believe the computer doesn't do any rounding(just truncation), but this truncation could mess up any rounding you tried to do within your code. To avoid the truncation you can do everything with whole numbers(integer values), its just more work. Any floating point math equation can be solved using whole numbers(multiplying everything out to get rid of the decimal). There is a similar problem with large integers when the values surpass what can be natively reprsented by the bit width(if its 32bit, something greater than 2^32) But thats much much less of an issue... DISCLAIMER: This is all simplified..I'm not into electrical engineering, not an expert in the area. Just taking a shot at answering the question :) hope this all helps - blueAdept | [reply] |