libsurfer/translation/
fixed_point.rs
1use num::{BigUint, One, Zero};
2use std::cmp::Ordering;
3use std::ops::BitAnd;
4
5pub(crate) fn big_uint_to_ufixed(uint: &BigUint, lg_scaling_factor: i64) -> String {
10 match lg_scaling_factor.cmp(&0) {
11 Ordering::Less => {
12 format!("{}", uint << (-lg_scaling_factor))
13 }
14 Ordering::Equal => format!("{}", uint),
15 Ordering::Greater => {
16 let mask = (BigUint::one() << lg_scaling_factor) - 1_u32;
17
18 let integer_part = uint >> lg_scaling_factor;
20 let mut remainder = uint.bitand(&mask);
21
22 if remainder.is_zero() {
23 integer_part.to_string() } else {
25 let mut fractional_part = String::new();
26
27 for _ in 0..lg_scaling_factor {
29 remainder *= 10_u32;
30 let digit = &remainder >> lg_scaling_factor;
31 fractional_part.push_str(&digit.to_string());
32 remainder &= &mask;
33
34 if remainder.is_zero() {
36 break;
37 }
38 }
39
40 format!("{}.{}", integer_part, fractional_part)
41 }
42 }
43 }
44}
45
46pub(crate) fn big_uint_to_sfixed(uint: &BigUint, num_bits: u64, lg_scaling_factor: i64) -> String {
52 if num_bits == 0 {
53 return "".to_string();
54 }
55 if uint.bit(num_bits - 1) {
56 let inverted_uint = (BigUint::one() << num_bits) - uint;
57 format!("-{}", big_uint_to_ufixed(&inverted_uint, lg_scaling_factor))
58 } else {
59 big_uint_to_ufixed(uint, lg_scaling_factor)
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use std::str::FromStr;
67
68 fn ucheck(value: impl Into<BigUint>, lg_scaling_factor: i64, expected: impl Into<String>) {
69 let value = value.into();
70 let result = big_uint_to_ufixed(&value, lg_scaling_factor);
71 assert_eq!(result, expected.into());
72 }
73
74 fn scheck(
75 value: impl Into<BigUint>,
76 num_bits: u64,
77 lg_scaling_factor: i64,
78 expected: impl Into<String>,
79 ) {
80 let value = value.into();
81 let result = big_uint_to_sfixed(&value, num_bits, lg_scaling_factor);
82 assert_eq!(result, expected.into());
83 }
84
85 #[test]
86 fn zero_scaling_factor() {
87 ucheck(32_u32, 0, "32");
88 scheck(32_u32, 6, 0, "-32");
89 }
90
91 #[test]
92 fn zero_width_signed() {
93 scheck(32_u32, 0, 0, "");
94 }
95
96 #[test]
97 fn test_exact_integer() {
98 ucheck(256_u32, 8, "1");
99 }
100
101 #[test]
102 fn test_fractional_value() {
103 ucheck(48225_u32, 8, "188.37890625");
104 ucheck(100_u32, 10, "0.09765625");
105 ucheck(8192_u32, 15, "0.25");
106 ucheck(16384_u32, 15, "0.5");
107 }
108
109 #[test]
110 fn test_large_value() {
111 ucheck(
112 BigUint::from_str("12345678901234567890").unwrap(),
113 20,
114 "11773756886705.9401416778564453125",
115 )
116 }
117
118 #[test]
119 fn test_value_less_than_one() {
120 ucheck(1_u32, 10, "0.0009765625")
121 }
122
123 #[test]
124 fn test_zero_value() {
125 ucheck(0_u32, 16, "0")
126 }
127
128 #[test]
129 fn test_negative_scaling_factor() {
130 ucheck(500_u32, -1, "1000")
131 }
132
133 #[test]
134 fn test_negative_fractional_value() {
135 scheck(0x1E000_u32, 17, 15, "-0.25");
136 scheck(0x1FF_u32, 9, 7, "-0.0078125");
137 }
138
139 #[test]
140 fn test_negative_exact_integer() {
141 scheck(0xFCC_u32, 12, 2, "-13");
142 scheck(0x100_u32, 9, 7, "-2");
143 scheck(256_u32, 9, 8, "-1")
144 }
145
146 #[test]
147 fn test_negative_non_signed_values() {
148 scheck(0x034_u32, 12, 2, "13");
149 scheck(0x02000_u32, 17, 15, "0.25");
150 scheck(0x0FF_u32, 9, 7, "1.9921875");
151 }
152
153 #[test]
154 fn large_bit_widths() {
155 ucheck(0x123456789ABCDEF0_u64, 20, "1250999896491.8044281005859375");
156 }
157}