From 8607a6b438af5ab4f5114618be4c7eaff9ffc1de Mon Sep 17 00:00:00 2001 From: Jens Date: Sun, 29 Mar 2026 22:51:25 +0200 Subject: [PATCH] Add sin/cos/tan operations and documentation --- conversions.go | 15 ++++++++------- conversions_test.go | 23 +++++++++++++++++++++++ operations.go | 26 ++++++++++++++++++++++++++ operations_test.go | 37 +++++++++++++++++++++++++++++++++++++ readme.md | 7 ++++++- 5 files changed, 100 insertions(+), 8 deletions(-) diff --git a/conversions.go b/conversions.go index 520e8b6..f453a61 100644 --- a/conversions.go +++ b/conversions.go @@ -179,20 +179,21 @@ func (a X80) ToInt64RoundZero() int64 { shiftCount := aExp - 0x403E if 0 <= shiftCount { aSig &= math.MaxInt64 - if a.high != 0xC03E || aSig != 0 { - Raise(ExceptionInvalid) - if !aSign || ((aExp == 0x7FFF) && aSig != 0) { - return math.MaxInt64 - } + if a.high == 0xC03E && aSig == 0 { + return math.MinInt64 } - return math.MaxInt64 + Raise(ExceptionInvalid) + if !aSign || ((aExp == 0x7FFF) && aSig != 0) { + return math.MaxInt64 + } + return math.MinInt64 } else if aExp < 0x3FFF { if aExp != 0 || aSig != 0 { Raise(ExceptionInexact) } return 0 } - z := int64(aSig) >> (-shiftCount) + z := int64(aSig >> (-shiftCount)) if uint64(aSig<<(shiftCount&63)) != 0 { Raise(ExceptionInexact) } diff --git a/conversions_test.go b/conversions_test.go index 9b3a6f7..2992755 100644 --- a/conversions_test.go +++ b/conversions_test.go @@ -79,6 +79,29 @@ func TestX80_ToInt64(t *testing.T) { } } +func TestX80_ToInt64RoundZero(t *testing.T) { + tests := []struct { + name string + a X80 + want int64 + }{ + {"zero", X80Zero, 0}, + {"1", X80One, 1}, + {"-1", X80MinusOne, -1}, + {"exact min int64", newFromHexString("C03E8000000000000000"), math.MinInt64}, + {"positive overflow", X80InfPos, math.MaxInt64}, + {"negative overflow", newFromHexString("C03F8000000000000000"), math.MinInt64}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := tt.a + if got := a.ToInt64RoundZero(); got != tt.want { + t.Errorf("X80.ToInt64RoundZero() = %v, want %v", got, tt.want) + } + }) + } +} + func TestInt64ToFloatX80(t *testing.T) { tests := []struct { name string diff --git a/operations.go b/operations.go index 19e7fd9..570b999 100644 --- a/operations.go +++ b/operations.go @@ -1,5 +1,7 @@ package float +import "math" + // RoundToInt rounds the extended double-precision floating-point value `a' to an integer, // and returns the result as an extended quadruple-precision floating-point // value. The operation is performed according to the IEC/IEEE Standard for @@ -589,3 +591,27 @@ func (a X80) Atan() X80 { return result } + +// Sin returns the sine of the extended double-precision floating-point value `a'. +func (a X80) Sin() X80 { + if a.IsNaN() { + return X80NaN + } + return Float64ToFloatX80(math.Sin(a.ToFloat64())) +} + +// Cos returns the cosine of the extended double-precision floating-point value `a'. +func (a X80) Cos() X80 { + if a.IsNaN() { + return X80NaN + } + return Float64ToFloatX80(math.Cos(a.ToFloat64())) +} + +// Tan returns the tangent of the extended double-precision floating-point value `a'. +func (a X80) Tan() X80 { + if a.IsNaN() { + return X80NaN + } + return Float64ToFloatX80(math.Tan(a.ToFloat64())) +} diff --git a/operations_test.go b/operations_test.go index 9eaa2f1..c7ca1cd 100644 --- a/operations_test.go +++ b/operations_test.go @@ -1,6 +1,7 @@ package float import ( + "math" "testing" ) @@ -162,6 +163,42 @@ func TestX80_Atan(t *testing.T) { }) } } + +func TestX80_SinCosTan(t *testing.T) { + tests := []struct { + name string + got X80 + want float64 + wantNaN bool + }{ + {"sin(0)", X80Zero.Sin(), 0, false}, + {"sin(pi/2)", X80Pi.Div(Int64ToFloatX80(2)).Sin(), 1, false}, + {"cos(0)", X80Zero.Cos(), 1, false}, + {"cos(pi)", X80Pi.Cos(), -1, false}, + {"tan(0)", X80Zero.Tan(), 0, false}, + {"tan(pi/4)", X80Pi.Div(Int64ToFloatX80(4)).Tan(), 1, false}, + {"sin(inf)", X80InfPos.Sin(), 0, true}, + {"cos(inf)", X80InfPos.Cos(), 0, true}, + {"tan(inf)", X80InfPos.Tan(), 0, true}, + {"sin(nan)", X80NaN.Sin(), 0, true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.got.ToFloat64() + if tt.wantNaN { + if !math.IsNaN(got) { + t.Fatalf("%s: expected NaN, got %v", tt.name, got) + } + return + } + if math.Abs(got-tt.want) > 1e-12 { + t.Fatalf("%s: got %v, want %v", tt.name, got, tt.want) + } + }) + } +} + func TestExceptionHandling(t *testing.T) { // Clear any existing exceptions ClearExceptions() diff --git a/readme.md b/readme.md index 96bc7d6..2c7ef6e 100644 --- a/readme.md +++ b/readme.md @@ -116,7 +116,7 @@ func main() { ## Features - **Full IEEE 754 Compliance**: Proper handling of 80-bit extended precision -- **Complete Arithmetic Operations**: Add, Sub, Mul, Div, Rem, Sqrt, Ln, Atan +- **Complete Arithmetic Operations**: Add, Sub, Mul, Div, Rem, Sqrt, Ln, Atan, Sin, Cos, Tan - **Type Conversions**: To/from int32, int64, float32, float64 - **String Formatting**: Binary, decimal, and hexadecimal representations - **Exception Handling**: IEEE 754 exception flags with customizable handlers @@ -217,6 +217,9 @@ type X80 struct { - `Sqrt() X80` - Square root - `Ln() X80` - Natural logarithm - `Atan() X80` - Arctangent +- `Sin() X80` - Sine +- `Cos() X80` - Cosine +- `Tan() X80` - Tangent #### Comparison Operations - `Eq(b X80) bool` - Equal @@ -227,7 +230,9 @@ type X80 struct { #### Conversion Operations - `ToInt32() int32` - Convert to 32-bit integer +- `ToInt32RoundZero() int32` - Convert to 32-bit integer with round-toward-zero semantics - `ToInt64() int64` - Convert to 64-bit integer +- `ToInt64RoundZero() int64` - Convert to 64-bit integer with round-toward-zero semantics - `ToFloat32() float32` - Convert to 32-bit float - `ToFloat64() float64` - Convert to 64-bit float - `String() string` - Convert to decimal string