Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pure Go implementation would be great #8

Open
rin01 opened this issue Dec 12, 2015 · 0 comments
Open

pure Go implementation would be great #8

rin01 opened this issue Dec 12, 2015 · 0 comments

Comments

@rin01
Copy link
Owner

rin01 commented Dec 12, 2015

So far, I am more or less satisfied with the API.
In particular, putting the status as part of the number seems very logical to me.

Unfortunately, this package is a thin wrapper around the C decNumber library, and this appears too much in the current API.
E.g. function names like QuadToString or ToInt32 just call the original C function with the same name, and I wanted this package to be a wrapper that doesn't hide the underlying C library.
But in the end, this package is not as Go-like as I would like.

So, it would be great to have a pure Go implementation of this package.

My conclusions are as follows.

Mantissa

I think it should be in binary, not in base-10 or base-1000.
Operations on binary numbers are faster than on the digits of a base-10 encoded mantissa.

The mantissa is made of unsigned 32 bits parts. Using 64 bits operations (+, -, *) on them makes addition, subtraction and multiplication easy.

Also, Crockford also has a binary mantissa in his dec64 type.
http://dec64.com/

// Quad is a 128 bits structure storing a floating point decimal number.
//
//  13 bits   E:    signed exponent   [-4096;4095]
//   1 bit    V:    special value (-Inf, Inf, Nan. Value of mantissa can discriminate between them)
//   1 bit    S:    sign
// 113 bits   M:    unsigned mantissa in base 2. Val max = 10 384 593 717 069 655 257 060 992 658 440 191      34 significant decimal digits
//
type Quad struct {
      m []uint32   // EEEEEEEE EEEEEVSM MMMMMMMM MMMMMMMM     MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM     MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM     MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM
      status uint16
}

Addition, subtraction, multiplication

These operations are easy to perform with the struct described above.
Multiplication is done into a 256 bits integer, made of 32-bits parts.

The biggest difficulty is to quantize the numbers, that is, to adjust the exponent, to make them equal for addition, or to left-shift the mantissa in case multiplication gives a result too large.

Quantization

Essentially, it is shifting left or right the mantissa by a power of ten.

Right-shift is easy, by multiplying it by 10, 100, 1000, etc. 10*x is (4*x + x)*2, which is perhaps faster than *10 mult operation.

Left-shift is difficult, as we need to divide by 10, 100, 1000, etc.
Left shift must comply with given rounding mode.

Note that code with just bit-shifting exists to divide by 10, 100, 1000. Iterating on them can give the desired shifting. There will be at most 10 iterations of division by 1000 for a Quad with 34 significant digits.

http://www.hackersdelight.org/divcMore.pdf

unsigned divu10(unsigned n) {
 unsigned q, r;
 q = (n >> 1) + (n >> 2);
 q = q + (q >> 4);
 q = q + (q >> 8);
 q = q + (q >> 16);
 q = q >> 3;
 r = n - q*10;
 return q + ((r + 6) >> 4);
// return q + (r > 9);
}

Division

http://www.slideserve.com/amaris/newton-raphson-and-goldschmidt-division-algorithm

Comparison between NewtonRaphson and Goldschmidt algorithms Accuracy:

• Division based on Newton’s method has a
quadratic convergence rate and is self-correcting.
Goldschmidt’s algorithm is not self-correcting;
namely, inaccuracies of intermediate
computations accumulate and cause the
computed result to drift away from the accurate
quotient.

So, NewtonRaphson is the best choice to me.

conversion binary to decimal string

Just by iterating constant subtractions for each power of ten (1e34, 1e33, ... 1000, 100, 10) gives each decimal digit.

implementing package private operators on []uint32

I thought it would be good to implement a int128 type with the add, sub, mult, div, right-shift, left-shift first, as the decimal 128 is in fact a int128 type with some fields (exponent, etc) stuffed in the higher part.

But now, I think it is better and more general to implement some operator functions, private to the package, that work on []uint32.

Such operators will have the signature as follows, and it panics if dest is too small, because we always know the size of dest.
E.g. for multiplication, if a and b are 128 bits, dest is 256 bits.

func add(dest []uint32, a []uint32, b []uint32)
func sub(dest []uint32, a []uint32, b []uint32)
func mul(dest []uint32, a []uint32, b []uint32)
func div(dest []uint32, a []uint32, b []uint32)

func tryQuantize(dest []uint32, a []uint32, requestedExp int, rounding RoundingMode) (exp int, flag StatusFlag)

The function tryQuantize tries to shift the number, so that the exponent becomes requestedExp, but this may be impossible. This function returns the number shifted as far as possible, and returns the exponent. It also returns a StatusFlag indicating events such as Underflow.

API changes

See rthornton128 comment, on 2015.12.15:
golang/go#12332 (comment)

misc. notes

The Python implementation of Decimal stores the mantissa as string, and makes a lot of conversion between string and long int, which is a builtin type of illimited precision (similar to Go big.Int type).

https://github.com/shopspring/decimal already uses big.Int type as mantissa.

An implementation for decimal 64 bits would be especially easy, as the mantissa would be int64, and we can use Go operators on int64. But for my own needs, a 64 bits decimal type has not enough precision,

when will I do it ?

Making a pure Go package is a little less difficult that I imagined.
Unfortunately, I am too busy for the moment. Perhaps end of 2016.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant