Skip to content

Documentation

Adam White edited this page May 22, 2024 · 2 revisions

Table of Contents









Introduction




What is it?

BigRational is an arbitrary precision floating point number.

The underlying representation is manipulated, displayed and reasoned about in base 10.

This is unlike most floating point data types, whos representation and manipulation is base-2 centric.




How does it work?

BigRational is a floating point number. As such, the underlying representation involves storing a Mantissa and an Exponent.

The Mantissa is stored as a BigInteger

The Exponent is stored as a Int32. If you happen to need an exponent larger than an Int32, please contact us. I'd love to know what your use case is!




Trigonometric Functions Implementation

All trigonometric functions are implemented using the Taylor Series. The Taylor Series is great as general solution to such functions. However, for inputs on certain functions, the Taylor series my take a long time to converge.

Generally, you won't notice a problem unless you are using the following methods: BigDecimal.Arcsin BigDecimal.Arccos

These methods, for certain inputs, can take take up to a second or so to run, which might not sound like a lot, but compared to other trigonometric functions which take tens of milliseconds to execute, yeah, it is a lot.

Quick and easy replacements for the Taylor series for these functions have not been forthcoming. I could go into a lengthy discussion about what solutions I have found and the problems with each, but in brief (for those of you who are interested):

From what I've found, possible replacements for the Taylor Series in terms of calculating trigonometric functions to arbitrary precision are:

  • Phebyshev polynomials
  • Pade approximations
  • Continued fractions

And each of these have their own problems which has prevented me from presenting a working implementation:

  • Continued fractions have a chicken-and-egg problem, in that they need to be evaluated in reverse order. This is not conducive to the desired behavior of incrementally calculating a more and more precise result with each iteration.

  • Pade approximation. This is very closely related to the Taylor series, and in fact the contribution here is a series of polynomials that you divide each term of the Taylor series by as a sort of correction. This leads to faster convergence, or rather it reduces or eliminates the asymptotic behavior of cases where it converges very slowly. Sounds good, right? Unfortunately, they way I understand it, this faces a chicken-and-egg problem too, in that to calculate the polynomials for each term, you must know the number of terms to use up front. This is even worse than the Continued Fraction situation above, of which I would be able to give a pretty-good rough estimation of how many iterations of continued fractions I would need for a given precision.

  • Chebyshev polynomials seem to be my best bet at this time, despite seeming the least helpful at the beginning of all this. I originally thought I would have to implement integrals, but I no longer think this to be the case. I have yet to give this an honest attempt.








Using BigDecimal




Setting the Precision

NOTE: Please read this section in its entirety. This is section is important to read and understand, as the default behavior of BigDecimal probably isn't what most would assume upon learning of the two properties mentioned below.

There are two static properties of BigDecimal that control its behavior that you need to be aware of. These properties are:

  • BigDecimal.Precision
  • BigDecimal.AlwaysTruncate

The default value for BigDecimal.Precision is 2000; The default value for BigDecimal.AlwaysTruncate is false.

When BigDecimal.AlwaysTruncate is true

When BigDecimal.AlwaysTruncate is true, BigDecimal will truncate the number of digits to the right of the decimal separator it is keeping track of after every arithmetic operation (if needed) to the number specified in the static property BigDecimal.Precision.

When BigDecimal.AlwaysTruncate is false

When BigDecimal.AlwaysTruncate is false, BigDecimal.Precision is basically ignored. Exceptions include any time an irrational number is involved (this includes all results from any of the trigonometric functions) and in the case of rational numbers with a repeating decimal representation. In these cases, BigRational will represent the number up to BigDecimal.Precision decimal places.

In the case of rational numbers with finite representation in base 10, arithmetic is carried out, and the number of decimal digits to the right of the decimal separator is allowed to grow to represent the result of the arithmetic operation to its full precision, even if that representation would contain more digits than the value specified in BigDecimal.Precision.

When BigDecimal.AlwaysTruncate is true, BigDecimal will constrain the number of digits to the right of the decimal separator to, at most, the value specified in BigDecimal.Precision.

Once again, the default value for BigDecimal.AlwaysTruncate is false.

The rational behind why this is the default behavior is that its better to keep the trailing digits by default, requiring the behavior that throws them away be set explicitly, than it is to toss away digits of precision of peoples' calculations by default, requiring that keeping the full precision be made explicit.

Surprisingly, the case of working with irrational numbers doesn't come up that often. That's because it difficult to construct truly (extends to max precision) irrational numbers. And you cant get irrational number using rational numbers and basic arithmetic operations. You'd have to start with Pi or e, or use the root operation or any of the trigonometric operations.

How BigDecimal.AlwaysTruncate affects performance

Setting BigDecimal.AlwaysTruncate = true; may significantly boost performance. If you are finding your calculations are taking a long time to return, and you find that your intermediate values have decimal representations that have grown obscenely long, setting BigDecimal.AlwaysTruncate = true; should see immediate gains in speed at the cost of a little precision.




Globalization, Cultures, Parsing and Number Formats

BigInteger supports international number formats! If you've tried to parse the decimal equivalent of 1/4 as "0,25", you may have encountered an exception. No worries, BigDecimal does support that. All you have to do to make that work is set the CurrentCulture to your current culture, or any culture who has the same number formatting rules as yours.

Doing so will affect the behavior of BigInteger.Parse and the ToString() override.

Here is an example of doing just that:

CultureInfo argentina = CultureInfo.GetCultureInfoByIetfLanguageTag("es-AR");
CultureInfo.CurrentCulture = argentina;

When parsing, the library uses the properties of CultureInfo.CurrentCulture.NumberFormat to parse a string into a BigDecimal, so it should do the right thing if you just set CultureInfo.CurrentCulture.

Note: This affects only the thread that runs such a statement. If your application is multi-threaded, consider setting DefaultThreadCurrentCulture instead.




Using in PowerShell

To use the BigDecimal assembly in PowerShell, first download the project and build it or download the NuGet package and use something like 7zip to open the .nupkg and extract the compiled .dll assembly to a location. In the examples below, it is assumed that the assembly has been extracted to the directory location C:\Temp\

Open a PowerShell prompt. Add the assembly type to the PowerShell session by using Add-Type:

Add-Type -Path C:\Temp\ExtendedNumerics.BigDecimal.dll

This then allows you to address BigDecimal using the fully qualified name in square brackets.

An example is in order:

$oneQuarter = [ExtendedNumerics.BigDecimal]::Parse("0.25");

Here we called the static Parse method on BigDecimal and set the result to a variable called $oneQuarter. If you type the variable $oneQuarter into the command prompt and hit enter, the way PowerShell displays the variable isnt very useful. To get a better representation of the value stored in $oneQuarter, we should call .ToString() on it:

$oneQuarter.ToString()

Another common way to create a BigDecimal instance is using the constructor. Starting with PowerShell version 5 and greater, constructors can be called by calling new as if it was a method on the type:

$two = [ExtendedNumerics.BigDecimal]::new(2);

Then you can instance methods on the instance variable:

$two.ToString()

Or static methods on the static type, passing required instance parameters:

$quotient = [ExtendedNumerics.BigDecimal]::Divide($oneQuarter, $two) $quotient.ToString()

Since the arithmetic operator overloads have been implemented on BigDecimal, that means we can also use this syntax:

$oneQuarter / $two

Or to better visualize the result:

($oneQuarter / $two).ToString()

Because the default precision of BigDecimal is so high, you may wish to tone it down to something that is a bit more reasonable to display should you try and print a repeating decimal expansion:

[ExtendedNumerics.BigDecimal]::Precision = 100

Hopefully that will be enough to get you going using the BigInteger assembly in PowerShell.




Storing in SQL

Want to store a BigDecimal in an SQL table and be able to sort such entries by their natural ordering? You're not alone. We have already had a discussion about this over on the project's Discussions page and a few solutions were offered. The best answer, of course, depends on what precisely you are looking for and which trade-offs you are willing to make. Here is one such solution to the problem and discussion of alternatives








Contributing




Bug reports & Feature Requests

Found a bug? Thought of a cool feature? GitHub allows submission and tracking of these items as 'Issues' (a bit of a misnomer when it comes to submitting Questions or Feature Requests).

First, please check that your 'issue' doesn't already exist by browsing existing 'issues'. If it does, please consider contributing to the open issue and the discussion taking place there instead of opening up a new issue for the same or very similar problem (or request or question). Feel free to add additional information related to reproducing a bug (if its a bug) or simply say 'Me too' if you also experienced that bug, would like to see that feature or also had that question.

If you can find no relevant existing issue, you can create a new issue and select the type of 'issue' you would like to create. Additional instructions are given on the relevant issue creation pages.




Questions & Discussions

Looking to ask a question or start a discussion on the philosophy of developing software? While you can ask a question via a GitHub 'issue', you might be better served by visiting the project's Discussions page.