~sbinet/star-tex

98d6207a8ebf218d27686361dde95aba1c674c67 — Sebastien Binet 4 months ago b697be5
font/fixed: first import

Signed-off-by: Sebastien Binet <s@sbinet.org>
A font/fixed/fixed.go => font/fixed/fixed.go +6 -0
@@ 0,0 1,6 @@
// Copyright ©2021 The star-tex 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 fixed provides types to work with fixed-point numbers.
package fixed // import "star-tex.org/x/tex/font/fixed"

A font/fixed/int12_20.go => font/fixed/int12_20.go +53 -0
@@ 0,0 1,53 @@
// Copyright ©2021 The star-tex 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 fixed

import (
	"fmt"
	"strconv"
)

// Int12_20 is a signed 12.20 fixed-point number.
//
// The integer part ranges from -2048 to 2047, inclusive. The
// fractional part has 20 bits of precision.
type Int12_20 uint32

// I12_20 returns the integer value i as an Int12_20.
//
// For example, passing the integer value 2 yields Int12_20(2097152).
func I12_20(v int) Int12_20 {
	return Int12_20(v << 20)
}

// ParseInt12_20 converts the string s to a signed 12.20 fixed-point number.
func ParseInt12_20(s string) (Int12_20, error) {
	f, err := strconv.ParseFloat(s, 32)
	if err != nil {
		return 0, err
	}
	return Int12_20(f * (1 << 20)), nil
}

func (x Int12_20) Float64() float64 {
	v := int32(x)
	return float64(v) / (1 << 20)
}

// String returns a human-readable representation of a 12.20 fixed-point number.
func (x Int12_20) String() string {
	const (
		shift = 12
		mask  = 1<<shift - 1
	)
	if x >= 0 {
		return fmt.Sprintf("%d:%02d", int32(x>>shift), int32(x&mask))
	}
	x = -x
	if x >= 0 {
		return fmt.Sprintf("-%d:%02d", int32(x>>shift), int32(x&mask))
	}
	return "-2048:00" // The minimum value is -(1<<(12-1)).
}

A font/fixed/int12_20_test.go => font/fixed/int12_20_test.go +38 -0
@@ 0,0 1,38 @@
// Copyright ©2021 The star-tex 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 fixed

import (
	"math"
	"testing"
)

func TestParseInt12_20(t *testing.T) {
	const tol = 1e-6
	for _, tc := range []struct {
		str  string
		want float64
	}{
		{"0", 0},
		{"100", 100},
		{"-100", -100},
		{"+100", +100},
		{"1.0", 1},
		{"1.2", 1.2},
		{"+1.2", +1.2},
		{"-1.2", -1.2},
	} {
		t.Run("", func(t *testing.T) {
			v, err := ParseInt12_20(tc.str)
			if err != nil {
				t.Fatalf("could not parse %q: %+v", tc.str, err)
			}
			got := v.Float64()
			if diff := math.Abs(got - tc.want); diff > tol {
				t.Fatalf("invalid 12:20 value: got=%v, want=%v (diff=%e v=%s)", got, tc.want, diff, v)
			}
		})
	}
}

A font/fixed/int16_16.go => font/fixed/int16_16.go +53 -0
@@ 0,0 1,53 @@
// Copyright ©2021 The star-tex 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 fixed

import (
	"fmt"
	"strconv"
)

// Int16_16 is a signed 16.16 fixed-point number.
//
// The integer part ranges from -32768 to 32767, inclusive. The
// fractional part has 20 bits of precision.
type Int16_16 uint32

// I16_16 returns the integer value i as an Int16_16.
//
// For example, passing the integer value 2 yields Int16_16(131072).
func I16_16(v int) Int16_16 {
	return Int16_16(v << 16)
}

// ParseInt16_16 converts the string s to a signed 16.16 fixed-point number.
func ParseInt16_16(s string) (Int16_16, error) {
	f, err := strconv.ParseFloat(s, 32)
	if err != nil {
		return 0, err
	}
	return Int16_16(f * (1 << 16)), nil
}

func (x Int16_16) Float64() float64 {
	v := int32(x)
	return float64(v) / (1 << 16)
}

// String returns a human-readable representation of a 16.16 fixed-point number.
func (x Int16_16) String() string {
	const (
		shift = 16
		mask  = 1<<shift - 1
	)
	if x >= 0 {
		return fmt.Sprintf("%d:%02d", int32(x>>shift), int32(x&mask))
	}
	x = -x
	if x >= 0 {
		return fmt.Sprintf("-%d:%02d", int32(x>>shift), int32(x&mask))
	}
	return "-32768:00" // The minimum value is -(1<<(16-1)).
}

A font/fixed/int16_16_test.go => font/fixed/int16_16_test.go +38 -0
@@ 0,0 1,38 @@
// Copyright ©2021 The star-tex 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 fixed

import (
	"math"
	"testing"
)

func TestParseInt16_16(t *testing.T) {
	const tol = 1e-5
	for _, tc := range []struct {
		str  string
		want float64
	}{
		{"0", 0},
		{"100", 100},
		{"-100", -100},
		{"+100", +100},
		{"1.0", 1},
		{"1.2", 1.2},
		{"+1.2", +1.2},
		{"-1.2", -1.2},
	} {
		t.Run("", func(t *testing.T) {
			v, err := ParseInt16_16(tc.str)
			if err != nil {
				t.Fatalf("could not parse %q: %+v", tc.str, err)
			}
			got := v.Float64()
			if diff := math.Abs(got - tc.want); diff > tol {
				t.Fatalf("invalid 16:16 value: got=%v, want=%v (diff=%e v=%s)", got, tc.want, diff, v)
			}
		})
	}
}