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

Posit draft #55

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
7 changes: 7 additions & 0 deletions posit/doc.go
Original file line number Diff line number Diff line change
@@ -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
143 changes: 143 additions & 0 deletions posit/posit.go
Original file line number Diff line number Diff line change
@@ -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
}
}
121 changes: 121 additions & 0 deletions posit/posit_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
}