diff --git a/posit/doc.go b/posit/doc.go new file mode 100644 index 0000000..6ebebfb --- /dev/null +++ b/posit/doc.go @@ -0,0 +1,7 @@ +// Copyright ©2021 The Gonum Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package posit implements a generalized unum type II implementation +// as a byte slice and provides primitives for working with posits. +package posit diff --git a/posit/posit.go b/posit/posit.go new file mode 100644 index 0000000..b263cfd --- /dev/null +++ b/posit/posit.go @@ -0,0 +1,143 @@ +// Copyright ©2021 The Gonum Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package posit + +import ( + "fmt" + "math" +) + +type Posit []byte + +func (p Posit) regime() (k int) { + // Regime lead bit. + const rbit = 0b0100_0000 + // Indicates if leading Regime bit is a one. + oneRun := rbit&p[0] != 0 +outer: + for i := range p { + for j := 0; j < 8; j++ { + // Ignore sign bit. + if i == 0 && j == 0 { + j++ + } + // If current bit is part of the run, add to r. + if (oneRun && p[i]&(1<<(7-j)) != 0) || (!oneRun && p[i]&(1<<(7-j)) == 0) { + k++ + } else { + break outer + } + } + } + if !oneRun { + k *= -1 + } else { + k-- + } + return k +} + +// sign returns true if posit negative +func (p Posit) sign() bool { return p[0]&(1<<7) != 0 } + +func (p Posit) bits() int { return 8 * len(p) } + +func (p Posit) String() string { return fmt.Sprintf("%08b", []byte(p)) } + +// es represents max amount of exponent bits that can be present in the posit. +// +// Values taken from http://www.johngustafson.net/pdfs/BeatingFloatingPoint.pdf +// (table 3) to match or exceed IEEE float dynamic range. +func (p Posit) es() (es int) { + switch p.bits() { + case 16: + es = 1 + case 32: + es = 3 + case 64: + es = 4 + case 128: + es = 7 + case 256: + es = 10 + default: + es = p.bits() / 16 // 8 bit posit has es == 0. + } + return es +} + +// exp returns the exponent part of the posit (2**e). +func (p Posit) exp() (exp int) { + // bits in front of exp. + flen := p.regimeLen() + 2 // sign and opposite bits included + es := p.es() + // Check if exp bits present for quick return. + if flen >= p.bits() || es == 0 { + return 0 + } + + expcount := 0 +outer: + for i := flen / 8; i < len(p); i++ { + for j := flen % 8; j < 8; j++ { + if expcount == es { + break outer + } + exp <<= 1 + exp |= (int(p[i]) & (1 << (7 - j))) >> (7 - j) + expcount++ + } + } + return exp +} + +// returns regime length for a given posit in number of bits. +func (p Posit) regimeLen() int { + r := p.regime() + if r < 0 { + return -r + } + return r + 1 +} + +// useed defines the midway point of accuracy from 1 to +inf and +// conversely from 1 to 0. It depends on es. +func (p Posit) useed() int { return 1 << (1 << (p.es())) } + +// fraction returns the numerator of the fraction part. +func (p Posit) fraction() (frac int) { + // bits in front of fraction. + flen := p.regimeLen() + p.es() + 2 // sign and opposite bits included + // Check if exp bits present for quick return. + if flen >= p.bits() { + return 0 + } + + for i := flen / 8; i < len(p); i++ { + for j := flen % 8; j < 8; j++ { + frac <<= 1 + frac |= (int(p[i]) & (1 << (7 - j))) >> (7 - j) + } + } + return frac +} + +func (p Posit) ToFloat64() float64 { + reg := float64(p.regime()) + useed := float64(p.useed()) + exp := 1 << p.exp() + return math.Pow(useed, reg) * float64(exp) * (1 + float64(p.fraction())/useed) +} + +// Format implements fmt.Formatter. +func (p Posit) Format(fs fmt.State, c rune) { + switch c { + case 'v', 'f': + fmt.Fprintf(fs, "%T{%f}", p, p.ToFloat64()) + default: + fmt.Fprintf(fs, "%%!%c(%T=%[2]v)", c, p) + return + } +} diff --git a/posit/posit_test.go b/posit/posit_test.go new file mode 100644 index 0000000..e8bb5da --- /dev/null +++ b/posit/posit_test.go @@ -0,0 +1,121 @@ +// Copyright ©2021 The Gonum Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package posit + +import ( + "math" + "testing" +) + +func TestRegime(t *testing.T) { + for _, test := range []struct { + p Posit + expect int + }{ + // posit8 + {Posit{0b_100_0000}, 0}, + {Posit{0b_111_0000}, 2}, + {Posit{0b_010_0000}, -1}, + {Posit{0b_111_1000}, 3}, + {Posit{0b_000_0100}, -4}, + + // posit16 + {Posit{0, 0b0010_0000}, -9}, + {Posit{0xff, 0b0010_0000}, 6}, + + // posit 32 + {Posit{0, 0, 0b1000_0001, 0}, -7 - 8}, + {Posit{0, 0, 0xff, 0}, -7 - 8}, + {Posit{0xff, 0xff, 0xff, 0}, 7 + 8 + 8 - 1}, + {Posit{0xff, 0, 0xff, 0}, 7 - 1}, + } { + got := test.p.regime() + if got != test.expect { + t.Errorf("posit %s expected regime %v, got %v", test.p, test.expect, got) + } + } +} + +func TestSign(t *testing.T) { + for _, test := range []struct { + p Posit + expect bool + }{ + {Posit{0b_1100_0000}, true}, + {Posit{0b_0111_0000}, false}, + {Posit{0b1010_0000}, true}, + {Posit{0b_0111_1000}, false}, + {Posit{0b_1000_0100}, true}, + {Posit{0, 0b0010_0000}, false}, + } { + got := test.p.sign() + if got != test.expect { + t.Errorf("posit %s expected sign %t, got %t", test.p, test.expect, got) + } + } +} + +func TestUseed(t *testing.T) { + for _, test := range []struct { + p Posit + }{ + {Posit{0b1100_0000}}, + + {Posit{0, 0b0010_0000}}, + {Posit{0, 0b0010_0000, 0}}, + } { + es := test.p.es() + expect := int(math.RoundToEven(math.Pow(2, math.Pow(2, float64(es))))) + got := test.p.useed() + if expect != got { + t.Errorf("posit %s expected useed %v, got %v", test.p, expect, got) + } + } +} + +func TestExp(t *testing.T) { + for _, test := range []struct { + p Posit + expect int + }{ + // posit16 has es == 1 + {Posit{0b_101_0000, 0}, 1}, + {Posit{0b_100_0000, 0}, 0}, + {Posit{0b_111_1001, 0}, 0}, + {Posit{0b_111_1011, 0}, 1}, + // posit 32 has es == 3 + {Posit{0, 0, 0b10111, 0}, 3}, + {Posit{0, 0, 0b11111, 0}, 7}, + } { + got := test.p.exp() + expect := test.expect + + if expect != got { + t.Errorf("posit %s expected exp %v, got %v", test.p, expect, got) + } + } +} +func TestFraction(t *testing.T) { + for _, test := range []struct { + p Posit + expect int + }{ + // posit16 has es == 1 + {Posit{0b_101_0000, 0}, 1}, + {Posit{0b_100_0000, 0}, 0}, + {Posit{0b_111_1001, 0}, 0}, + {Posit{0b_111_1011, 0}, 1}, + // posit 32 has es == 3 + {Posit{0, 0, 0b10111, 0}, 3}, + {Posit{0, 0, 0b11111, 0}, 7}, + } { + got := test.p.fraction() + expect := test.expect + + if expect != got { + t.Errorf("posit %s expected exp %v, got %v", test.p, expect, got) + } + } +}