Source file src/simd/internal/spec/mathlib.go

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package spec
     6  
     7  import (
     8  	"math"
     9  	"math/bits"
    10  )
    11  
    12  func isSigned[T Ints | Uints]() bool {
    13  	return T(0)-1 < 0
    14  }
    15  
    16  func maxVal[T Ints | Uints]() T {
    17  	if isSigned[T]() {
    18  		var zero T
    19  		switch any(zero).(type) {
    20  		case int8:
    21  			return any(int8(math.MaxInt8)).(T)
    22  		case int16:
    23  			return any(int16(math.MaxInt16)).(T)
    24  		case int32:
    25  			return any(int32(math.MaxInt32)).(T)
    26  		case int64:
    27  			return any(int64(math.MaxInt64)).(T)
    28  		}
    29  		panic("unhandled type")
    30  	}
    31  	return ^T(0)
    32  }
    33  
    34  func minVal[T Ints | Uints]() T {
    35  	if isSigned[T]() {
    36  		return ^maxVal[T]()
    37  	}
    38  	return 0
    39  }
    40  
    41  // saturate converts x to type T, with saturation.
    42  func saturate[T Ints | Uints, U Ints | Uints](x T) U {
    43  	if isSigned[T]() {
    44  		return saturateS[U](int64(x))
    45  	}
    46  	return saturateU[U](uint64(x))
    47  }
    48  
    49  // saturateS converts signed x to type T, with saturation.
    50  func saturateS[T Ints | Uints](x int64) T {
    51  	if int64(T(x)) == x && (x >= 0 || isSigned[T]()) {
    52  		// It's in range.
    53  		return T(x)
    54  	}
    55  
    56  	// Out of range
    57  	if x > 0 {
    58  		return maxVal[T]()
    59  	}
    60  	return minVal[T]()
    61  }
    62  
    63  // saturateU converts unsigned x to type T, with saturation.
    64  func saturateU[T Ints | Uints](x uint64) T {
    65  	if x < uint64(maxVal[T]()) {
    66  		return T(x)
    67  	}
    68  	return maxVal[T]()
    69  }
    70  
    71  func addSaturated[T Ints | Uints](x, y T) T {
    72  	if isSigned[T]() {
    73  		return saturateS[T](addSaturatedSSS64(int64(x), int64(y)))
    74  	}
    75  	sum, carry := bits.Add64(uint64(x), uint64(y), 0)
    76  	if carry > 0 {
    77  		return maxVal[T]()
    78  	}
    79  	return saturateU[T](sum)
    80  }
    81  
    82  func addSaturatedSSS64(x, y int64) int64 {
    83  	sum := x + y
    84  
    85  	// Overflow can only happen if x and y have the same sign, and the sum has a
    86  	// different sign.
    87  	//
    88  	// (x ^ sum) & (y ^ sum) checks if the sign bit of sum matches neither x nor y.
    89  	if (x^sum)&(y^sum) < 0 {
    90  		if x > 0 {
    91  			return math.MaxInt64
    92  		}
    93  		return math.MinInt64
    94  	}
    95  
    96  	return sum
    97  }
    98  
    99  func mulSaturatedUSS[X Uints, Y Ints](x X, y Y) Y {
   100  	// Expand to 64 bits and perform saturated multiplication
   101  	z := mulSaturatedUSS64(uint64(x), int64(y))
   102  	return saturateS[Y](z)
   103  }
   104  
   105  func mulSaturatedUSS64(x uint64, y int64) int64 {
   106  	if x == 0 || y == 0 {
   107  		return 0
   108  	}
   109  
   110  	// Get the absolute value of i as a uint64
   111  	var absI uint64
   112  	if y == math.MinInt64 {
   113  		absI = math.MaxInt64 + 1
   114  	} else if y < 0 {
   115  		absI = uint64(-y)
   116  	} else {
   117  		absI = uint64(y)
   118  	}
   119  
   120  	// 128-bit multiplication
   121  	hi, lo := bits.Mul64(x, absI)
   122  
   123  	if y > 0 {
   124  		// Positive result. Check for overflow.
   125  		if hi > 0 || lo >= math.MaxInt64 {
   126  			return math.MaxInt64
   127  		}
   128  		return int64(lo)
   129  	} else {
   130  		// Negative result. Check for underflow.
   131  		if hi > 0 || lo >= uint64(math.MaxInt64)+1 {
   132  			return math.MinInt64
   133  		}
   134  		return -int64(lo)
   135  	}
   136  }
   137  

View as plain text