fixed.odin 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package math_fixed
  2. import "core:math"
  3. import "core:strconv"
  4. import "base:intrinsics"
  5. _, _, _ :: intrinsics, strconv, math
  6. Fixed :: struct($Backing: typeid, $Fraction_Width: uint)
  7. where
  8. intrinsics.type_is_integer(Backing),
  9. 0 <= Fraction_Width,
  10. Fraction_Width <= 8*size_of(Backing) {
  11. i: Backing,
  12. }
  13. Fixed4_4 :: distinct Fixed(i8, 4)
  14. Fixed5_3 :: distinct Fixed(i8, 3)
  15. Fixed6_2 :: distinct Fixed(i8, 2)
  16. Fixed7_1 :: distinct Fixed(i8, 1)
  17. Fixed8_8 :: distinct Fixed(i16, 8)
  18. Fixed13_3 :: distinct Fixed(i16, 3)
  19. Fixed16_16 :: distinct Fixed(i32, 16)
  20. Fixed26_6 :: distinct Fixed(i32, 6)
  21. Fixed32_32 :: distinct Fixed(i64, 32)
  22. Fixed52_12 :: distinct Fixed(i64, 12)
  23. init_from_f64 :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), val: f64) {
  24. i, f := math.modf(math.abs(val))
  25. x.i = Backing(f * (1<<Fraction_Width))
  26. x.i &= 1<<Fraction_Width - 1
  27. x.i |= Backing(i) << Fraction_Width
  28. if val < 0 {
  29. x.i *= -1
  30. }
  31. }
  32. init_from_parts :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), integer, fraction: Backing) {
  33. i, f := math.modf(val)
  34. x.i = fraction
  35. x.i &= 1<<Fraction_Width - 1
  36. x.i |= integer
  37. }
  38. to_f64 :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> f64 {
  39. sign := -1.0 if x.i < 0 else 1.0
  40. num := math.abs(x.i)
  41. res := f64(num >> Fraction_Width)
  42. res += f64(num & (1<<Fraction_Width-1)) / f64(1<<Fraction_Width)
  43. return res * sign
  44. }
  45. @(require_results)
  46. add :: proc(x, y: $T/Fixed) -> T {
  47. return {x.i + y.i}
  48. }
  49. @(require_results)
  50. sub :: proc(x, y: $T/Fixed) -> T {
  51. return {x.i - y.i}
  52. }
  53. @(require_results)
  54. mul :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
  55. z.i = intrinsics.fixed_point_mul(x.i, y.i, Fraction_Width)
  56. return
  57. }
  58. @(require_results)
  59. mul_sat :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
  60. z.i = intrinsics.fixed_point_mul_sat(x.i, y.i, Fraction_Width)
  61. return
  62. }
  63. @(require_results)
  64. div :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
  65. z.i = intrinsics.fixed_point_div(x.i, y.i, Fraction_Width)
  66. return
  67. }
  68. @(require_results)
  69. div_sat :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
  70. z.i = intrinsics.fixed_point_div_sat(x.i, y.i, Fraction_Width)
  71. return
  72. }
  73. @(require_results)
  74. floor :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
  75. if x.i >= 0 {
  76. return x.i >> Fraction_Width
  77. } else {
  78. return (x.i - (1 << (Fraction_Width - 1)) + (1 << (Fraction_Width - 2))) >> Fraction_Width
  79. }
  80. }
  81. @(require_results)
  82. ceil :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
  83. return (x.i + (1 << Fraction_Width - 1)) >> Fraction_Width
  84. }
  85. @(require_results)
  86. round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
  87. return (x.i + (1 << (Fraction_Width - 1))) >> Fraction_Width
  88. }
  89. @(require_results)
  90. append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
  91. x := x
  92. buf: [48]byte
  93. i := 0
  94. if x.i < 0 {
  95. buf[i] = '-'
  96. i += 1
  97. x.i = -x.i
  98. }
  99. integer := x.i >> Fraction_Width
  100. fraction := x.i & (1<<Fraction_Width - 1)
  101. s := strconv.append_uint(buf[i:], u64(integer), 10)
  102. i += len(s)
  103. if fraction != 0 {
  104. buf[i] = '.'
  105. i += 1
  106. for fraction > 0 {
  107. fraction *= 10
  108. buf[i] = byte('0' + (fraction>>Fraction_Width))
  109. i += 1
  110. fraction &= 1<<Fraction_Width - 1
  111. }
  112. }
  113. n := copy(dst, buf[:i])
  114. return string(dst[:i])
  115. }
  116. @(require_results)
  117. to_string :: proc(x: $T/Fixed($Backing, $Fraction_Width), allocator := context.allocator) -> string {
  118. buf: [48]byte
  119. s := append(buf[:], x)
  120. str := make([]byte, len(s), allocator)
  121. copy(str, s)
  122. return string(str)
  123. }