fixed.odin 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. // Fixed-point rational numbers and conversion to/from `f64`.
  2. package math_fixed
  3. import "core:math"
  4. import "core:strconv"
  5. import "base:intrinsics"
  6. _, _, _ :: intrinsics, strconv, math
  7. Fixed :: struct($Backing: typeid, $Fraction_Width: uint)
  8. where
  9. intrinsics.type_is_integer(Backing),
  10. 0 <= Fraction_Width,
  11. Fraction_Width <= 8*size_of(Backing) {
  12. i: Backing,
  13. }
  14. Fixed4_4 :: distinct Fixed(i8, 4)
  15. Fixed5_3 :: distinct Fixed(i8, 3)
  16. Fixed6_2 :: distinct Fixed(i8, 2)
  17. Fixed7_1 :: distinct Fixed(i8, 1)
  18. Fixed8_8 :: distinct Fixed(i16, 8)
  19. Fixed13_3 :: distinct Fixed(i16, 3)
  20. Fixed16_16 :: distinct Fixed(i32, 16)
  21. Fixed26_6 :: distinct Fixed(i32, 6)
  22. Fixed32_32 :: distinct Fixed(i64, 32)
  23. Fixed52_12 :: distinct Fixed(i64, 12)
  24. init_from_f64 :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), val: f64) {
  25. i, f := math.modf(math.abs(val))
  26. x.i = Backing(f * (1<<Fraction_Width))
  27. x.i &= 1<<Fraction_Width - 1
  28. x.i |= Backing(i) << Fraction_Width
  29. if val < 0 {
  30. x.i *= -1
  31. }
  32. }
  33. init_from_parts :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), integer, fraction: Backing) {
  34. x.i = fraction
  35. x.i &= 1<<Fraction_Width - 1
  36. x.i |= (integer << Fraction_Width)
  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. write :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
  91. Integer_Width :: 8*size_of(Backing) - Fraction_Width
  92. x := x
  93. buf: [48]byte
  94. i := 0
  95. if !intrinsics.type_is_unsigned(Backing) && x.i == min(Backing) {
  96. // edge case handling for signed numbers
  97. buf[i] = '-'
  98. i += 1
  99. i += copy(buf[i:], _power_of_two_table[Integer_Width])
  100. } else {
  101. if x.i < 0 {
  102. buf[i] = '-'
  103. i += 1
  104. x.i = -x.i
  105. }
  106. when size_of(Backing) < 16 {
  107. T :: u64
  108. write_uint :: strconv.write_uint
  109. } else {
  110. T :: u128
  111. write_uint :: strconv.write_u128
  112. }
  113. integer := T(x.i) >> Fraction_Width
  114. fraction := T(x.i) & (1<<Fraction_Width - 1)
  115. s := write_uint(buf[i:], integer, 10)
  116. i += len(s)
  117. if fraction != 0 {
  118. buf[i] = '.'
  119. i += 1
  120. for fraction > 0 {
  121. fraction *= 10
  122. buf[i] = byte('0' + (fraction>>Fraction_Width) % 10)
  123. i += 1
  124. fraction &= 1<<Fraction_Width - 1
  125. }
  126. }
  127. }
  128. n := copy(dst, buf[:i])
  129. return string(dst[:i])
  130. }
  131. @(require_results)
  132. to_string :: proc(x: $T/Fixed($Backing, $Fraction_Width), allocator := context.allocator) -> string {
  133. buf: [48]byte
  134. s := write(buf[:], x)
  135. str := make([]byte, len(s), allocator)
  136. copy(str, s)
  137. return string(str)
  138. }
  139. @(private)
  140. _power_of_two_table := [129]string{
  141. "0.5",
  142. "1",
  143. "2",
  144. "4",
  145. "8",
  146. "16",
  147. "32",
  148. "64",
  149. "128",
  150. "256",
  151. "512",
  152. "1024",
  153. "2048",
  154. "4096",
  155. "8192",
  156. "16384",
  157. "32768",
  158. "65536",
  159. "131072",
  160. "262144",
  161. "524288",
  162. "1048576",
  163. "2097152",
  164. "4194304",
  165. "8388608",
  166. "16777216",
  167. "33554432",
  168. "67108864",
  169. "134217728",
  170. "268435456",
  171. "536870912",
  172. "1073741824",
  173. "2147483648",
  174. "4294967296",
  175. "8589934592",
  176. "17179869184",
  177. "34359738368",
  178. "68719476736",
  179. "137438953472",
  180. "274877906944",
  181. "549755813888",
  182. "1099511627776",
  183. "2199023255552",
  184. "4398046511104",
  185. "8796093022208",
  186. "17592186044416",
  187. "35184372088832",
  188. "70368744177664",
  189. "140737488355328",
  190. "281474976710656",
  191. "562949953421312",
  192. "1125899906842624",
  193. "2251799813685248",
  194. "4503599627370496",
  195. "9007199254740992",
  196. "18014398509481984",
  197. "36028797018963968",
  198. "72057594037927936",
  199. "144115188075855872",
  200. "288230376151711744",
  201. "576460752303423488",
  202. "1152921504606846976",
  203. "2305843009213693952",
  204. "4611686018427387904",
  205. "9223372036854775808",
  206. "18446744073709551616",
  207. "36893488147419103232",
  208. "73786976294838206464",
  209. "147573952589676412928",
  210. "295147905179352825856",
  211. "590295810358705651712",
  212. "1180591620717411303424",
  213. "2361183241434822606848",
  214. "4722366482869645213696",
  215. "9444732965739290427392",
  216. "18889465931478580854784",
  217. "37778931862957161709568",
  218. "75557863725914323419136",
  219. "151115727451828646838272",
  220. "302231454903657293676544",
  221. "604462909807314587353088",
  222. "1208925819614629174706176",
  223. "2417851639229258349412352",
  224. "4835703278458516698824704",
  225. "9671406556917033397649408",
  226. "19342813113834066795298816",
  227. "38685626227668133590597632",
  228. "77371252455336267181195264",
  229. "154742504910672534362390528",
  230. "309485009821345068724781056",
  231. "618970019642690137449562112",
  232. "1237940039285380274899124224",
  233. "2475880078570760549798248448",
  234. "4951760157141521099596496896",
  235. "9903520314283042199192993792",
  236. "19807040628566084398385987584",
  237. "39614081257132168796771975168",
  238. "79228162514264337593543950336",
  239. "158456325028528675187087900672",
  240. "316912650057057350374175801344",
  241. "633825300114114700748351602688",
  242. "1267650600228229401496703205376",
  243. "2535301200456458802993406410752",
  244. "5070602400912917605986812821504",
  245. "10141204801825835211973625643008",
  246. "20282409603651670423947251286016",
  247. "40564819207303340847894502572032",
  248. "81129638414606681695789005144064",
  249. "162259276829213363391578010288128",
  250. "324518553658426726783156020576256",
  251. "649037107316853453566312041152512",
  252. "1298074214633706907132624082305024",
  253. "2596148429267413814265248164610048",
  254. "5192296858534827628530496329220096",
  255. "10384593717069655257060992658440192",
  256. "20769187434139310514121985316880384",
  257. "41538374868278621028243970633760768",
  258. "83076749736557242056487941267521536",
  259. "166153499473114484112975882535043072",
  260. "332306998946228968225951765070086144",
  261. "664613997892457936451903530140172288",
  262. "1329227995784915872903807060280344576",
  263. "2658455991569831745807614120560689152",
  264. "5316911983139663491615228241121378304",
  265. "10633823966279326983230456482242756608",
  266. "21267647932558653966460912964485513216",
  267. "42535295865117307932921825928971026432",
  268. "85070591730234615865843651857942052864",
  269. "170141183460469231731687303715884105728",
  270. }
  271. @(deprecated="Use write instead")
  272. append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
  273. return write(dst, x)
  274. }