[READ-ONLY] Mirror of https://github.com/probablykasper/cpc. Text calculator with support for units and conversion cpc.kasper.space
calculator cli conversion converter library math package units units-converter wasm website
0

Configure Feed

Select the types of activity you want to include in your feed.

Migrate fastnum 0.2 → 0.7.5 and adopt native math/rounding APIs (#45)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Kasper <kasperkh.kh@gmail.com>

authored by

Joel Natividad
Claude Opus 4.8 (1M context)
Kasper
and committed by
GitHub
(Jun 20, 2026, 12:30 AM +0200) bd59e6a0 49b05d08

+128 -108
+99 -62
Cargo.lock
··· 4 4 5 5 [[package]] 6 6 name = "aho-corasick" 7 - version = "1.1.3" 7 + version = "1.1.4" 8 8 source = "registry+https://github.com/rust-lang/crates.io-index" 9 - checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 9 + checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" 10 10 dependencies = [ 11 11 "memchr", 12 12 ] 13 13 14 14 [[package]] 15 15 name = "autocfg" 16 - version = "1.4.0" 16 + version = "1.5.1" 17 17 source = "registry+https://github.com/rust-lang/crates.io-index" 18 - checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 18 + checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" 19 19 20 20 [[package]] 21 21 name = "bnum" ··· 25 25 26 26 [[package]] 27 27 name = "bumpalo" 28 - version = "3.17.0" 28 + version = "3.20.3" 29 29 source = "registry+https://github.com/rust-lang/crates.io-index" 30 - checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 30 + checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" 31 31 32 32 [[package]] 33 33 name = "cfg-if" 34 - version = "1.0.0" 34 + version = "1.0.4" 35 35 source = "registry+https://github.com/rust-lang/crates.io-index" 36 - checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 36 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 37 37 38 38 [[package]] 39 39 name = "console_error_panic_hook" ··· 60 60 61 61 [[package]] 62 62 name = "fastnum" 63 - version = "0.2.9" 63 + version = "0.7.5" 64 64 source = "registry+https://github.com/rust-lang/crates.io-index" 65 - checksum = "b875e26379edd7866a74cec720c6ae7bea3107aaf9fd3b14d7449d87d4c1986b" 65 + checksum = "020d1b59a944bc239d79903fbac2eda2365138b44890c27979562f6592059dcd" 66 66 dependencies = [ 67 - "autocfg", 68 67 "bnum", 68 + "num-integer", 69 + "num-traits", 70 + ] 71 + 72 + [[package]] 73 + name = "futures-core" 74 + version = "0.3.32" 75 + source = "registry+https://github.com/rust-lang/crates.io-index" 76 + checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" 77 + 78 + [[package]] 79 + name = "futures-task" 80 + version = "0.3.32" 81 + source = "registry+https://github.com/rust-lang/crates.io-index" 82 + checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" 83 + 84 + [[package]] 85 + name = "futures-util" 86 + version = "0.3.32" 87 + source = "registry+https://github.com/rust-lang/crates.io-index" 88 + checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" 89 + dependencies = [ 90 + "futures-core", 91 + "futures-task", 92 + "pin-project-lite", 93 + "slab", 69 94 ] 70 95 71 96 [[package]] 72 97 name = "js-sys" 73 - version = "0.3.77" 98 + version = "0.3.102" 74 99 source = "registry+https://github.com/rust-lang/crates.io-index" 75 - checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 100 + checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" 76 101 dependencies = [ 77 - "once_cell", 102 + "cfg-if", 103 + "futures-util", 78 104 "wasm-bindgen", 79 105 ] 80 106 81 107 [[package]] 82 - name = "log" 83 - version = "0.4.27" 108 + name = "memchr" 109 + version = "2.8.2" 84 110 source = "registry+https://github.com/rust-lang/crates.io-index" 85 - checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 111 + checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" 86 112 87 113 [[package]] 88 - name = "memchr" 89 - version = "2.7.4" 114 + name = "num-integer" 115 + version = "0.1.46" 90 116 source = "registry+https://github.com/rust-lang/crates.io-index" 91 - checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 117 + checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 118 + dependencies = [ 119 + "num-traits", 120 + ] 121 + 122 + [[package]] 123 + name = "num-traits" 124 + version = "0.2.19" 125 + source = "registry+https://github.com/rust-lang/crates.io-index" 126 + checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 127 + dependencies = [ 128 + "autocfg", 129 + ] 92 130 93 131 [[package]] 94 132 name = "once_cell" 95 - version = "1.21.3" 133 + version = "1.21.4" 134 + source = "registry+https://github.com/rust-lang/crates.io-index" 135 + checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" 136 + 137 + [[package]] 138 + name = "pin-project-lite" 139 + version = "0.2.17" 96 140 source = "registry+https://github.com/rust-lang/crates.io-index" 97 - checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 141 + checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" 98 142 99 143 [[package]] 100 144 name = "proc-macro2" 101 - version = "1.0.95" 145 + version = "1.0.106" 102 146 source = "registry+https://github.com/rust-lang/crates.io-index" 103 - checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 147 + checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" 104 148 dependencies = [ 105 149 "unicode-ident", 106 150 ] 107 151 108 152 [[package]] 109 153 name = "quote" 110 - version = "1.0.40" 154 + version = "1.0.45" 111 155 source = "registry+https://github.com/rust-lang/crates.io-index" 112 - checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 156 + checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" 113 157 dependencies = [ 114 158 "proc-macro2", 115 159 ] 116 160 117 161 [[package]] 118 162 name = "regex" 119 - version = "1.11.1" 163 + version = "1.12.4" 120 164 source = "registry+https://github.com/rust-lang/crates.io-index" 121 - checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 165 + checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" 122 166 dependencies = [ 123 167 "aho-corasick", 124 168 "memchr", ··· 128 172 129 173 [[package]] 130 174 name = "regex-automata" 131 - version = "0.4.9" 175 + version = "0.4.14" 132 176 source = "registry+https://github.com/rust-lang/crates.io-index" 133 - checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 177 + checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" 134 178 dependencies = [ 135 179 "aho-corasick", 136 180 "memchr", ··· 139 183 140 184 [[package]] 141 185 name = "regex-syntax" 142 - version = "0.8.5" 186 + version = "0.8.11" 143 187 source = "registry+https://github.com/rust-lang/crates.io-index" 144 - checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 188 + checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" 145 189 146 190 [[package]] 147 191 name = "rustversion" 148 - version = "1.0.21" 192 + version = "1.0.22" 149 193 source = "registry+https://github.com/rust-lang/crates.io-index" 150 - checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" 194 + checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 195 + 196 + [[package]] 197 + name = "slab" 198 + version = "0.4.12" 199 + source = "registry+https://github.com/rust-lang/crates.io-index" 200 + checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" 151 201 152 202 [[package]] 153 203 name = "syn" 154 - version = "2.0.101" 204 + version = "2.0.118" 155 205 source = "registry+https://github.com/rust-lang/crates.io-index" 156 - checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 206 + checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" 157 207 dependencies = [ 158 208 "proc-macro2", 159 209 "quote", ··· 162 212 163 213 [[package]] 164 214 name = "unicode-ident" 165 - version = "1.0.18" 215 + version = "1.0.24" 166 216 source = "registry+https://github.com/rust-lang/crates.io-index" 167 - checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 217 + checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" 168 218 169 219 [[package]] 170 220 name = "unicode-segmentation" 171 - version = "1.12.0" 221 + version = "1.13.3" 172 222 source = "registry+https://github.com/rust-lang/crates.io-index" 173 - checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 223 + checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" 174 224 175 225 [[package]] 176 226 name = "wasm-bindgen" 177 - version = "0.2.100" 227 + version = "0.2.125" 178 228 source = "registry+https://github.com/rust-lang/crates.io-index" 179 - checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 229 + checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" 180 230 dependencies = [ 181 231 "cfg-if", 182 232 "once_cell", 183 233 "rustversion", 184 234 "wasm-bindgen-macro", 185 - ] 186 - 187 - [[package]] 188 - name = "wasm-bindgen-backend" 189 - version = "0.2.100" 190 - source = "registry+https://github.com/rust-lang/crates.io-index" 191 - checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 192 - dependencies = [ 193 - "bumpalo", 194 - "log", 195 - "proc-macro2", 196 - "quote", 197 - "syn", 198 235 "wasm-bindgen-shared", 199 236 ] 200 237 201 238 [[package]] 202 239 name = "wasm-bindgen-macro" 203 - version = "0.2.100" 240 + version = "0.2.125" 204 241 source = "registry+https://github.com/rust-lang/crates.io-index" 205 - checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 242 + checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" 206 243 dependencies = [ 207 244 "quote", 208 245 "wasm-bindgen-macro-support", ··· 210 247 211 248 [[package]] 212 249 name = "wasm-bindgen-macro-support" 213 - version = "0.2.100" 250 + version = "0.2.125" 214 251 source = "registry+https://github.com/rust-lang/crates.io-index" 215 - checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 252 + checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" 216 253 dependencies = [ 254 + "bumpalo", 217 255 "proc-macro2", 218 256 "quote", 219 257 "syn", 220 - "wasm-bindgen-backend", 221 258 "wasm-bindgen-shared", 222 259 ] 223 260 224 261 [[package]] 225 262 name = "wasm-bindgen-shared" 226 - version = "0.2.100" 263 + version = "0.2.125" 227 264 source = "registry+https://github.com/rust-lang/crates.io-index" 228 - checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 265 + checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" 229 266 dependencies = [ 230 267 "unicode-ident", 231 268 ]
+2 -2
Cargo.toml
··· 22 22 crate-type = ["cdylib", "rlib"] 23 23 24 24 [dependencies] 25 - fastnum = "0.2" 26 - unicode-segmentation = "1.12" 25 + fastnum = "0.7" 26 + unicode-segmentation = "1.13" 27 27 web-time = "1.1.0" 28 28 29 29 [target.'cfg(target_arch = "wasm32")'.dependencies]
+3 -3
README.md
··· 74 74 use cpc::eval; 75 75 use cpc::units::Unit; 76 76 77 - match eval("3m + 1cm", true, Unit::Celsius, false) { 77 + match eval("3m + 1cm", true, false) { 78 78 Ok(answer) => { 79 79 // answer: Number { value: 301, unit: Unit::Centimeter } 80 80 println!("Evaluated value: {} {:?}", answer.value, answer.unit) ··· 133 133 // ... 134 134 135 135 create_units!( 136 - Nanosecond: (Time, d128!(1)), 137 - Microsecond: (Time, d128!(1000)), 136 + Nanosecond: (Time, d!(1), "nanosecond", "nanoseconds"), 137 + Microsecond: (Time, d!(1000), "microsecond", "microseconds"), 138 138 // etc 139 139 ) 140 140 ```
+24 -41
src/evaluator.rs
··· 7 7 use crate::TextOperator::{Of, To}; 8 8 use crate::UnaryOperator::{Factorial, Percent}; 9 9 use crate::{Number, Token}; 10 - use fastnum::{dec128 as d, D128}; 10 + use fastnum::{dec128 as d, decimal::RoundingMode, D128}; 11 11 12 12 /// Evaluate an [`AstNode`] into a [`Number`] 13 13 pub fn evaluate(ast: &AstNode) -> Result<Number, String> { ··· 26 26 27 27 /// Returns the square root of a [`struct@d128`] 28 28 pub fn sqrt(input: D128) -> D128 { 29 - let mut n = d!(1); 30 - let half = d!(0.5); 31 - for _ in 0..10 { 32 - n = (n + input / n) * half; 33 - } 34 - n 29 + input.sqrt() 35 30 } 36 31 37 32 /// Returns the cube root of a [`struct@d128`] 38 33 pub fn cbrt(input: D128) -> D128 { 39 - let mut n: D128 = input; 40 - // hope that 20 iterations makes it accurate enough 41 - let three = d!(3); 42 - for _ in 0..20 { 43 - let z2 = n * n; 44 - n = n - ((n * z2 - input) / (three * z2)); 45 - } 46 - n 34 + input.cbrt() 47 35 } 48 36 49 37 /// Returns the sine of a [`struct@d128`] 50 38 pub fn sin(input: D128) -> D128 { 51 - let result =input.sin(); 52 - match result.is_zero() { 53 - true => D128::ZERO, 54 - false => result, 39 + let result = input.sin(); 40 + // D128::PI is a finite-precision approximation of pi, so sin(pi) lands a few 41 + // ulp away from zero rather than exactly zero. Snap sub-precision residue to 0. 42 + if result.abs() < d!(1E-30) { 43 + D128::ZERO 44 + } else { 45 + result 55 46 } 56 47 } 57 48 ··· 126 117 } 127 118 } 128 119 Round => { 129 - // .quantize() rounds .5 to nearest even integer, so we correct that 130 - let mut result = child_answer.value.quantize(d!(1)); 131 - let rounding_change = result - child_answer.value; 132 - // If the result was rounded down by 0.5, correct by +1 133 - if rounding_change == d!(-0.5) { 134 - result += d!(1); 135 - } 120 + // Round half away from zero (HalfUp) so round(2.5) == 3, not 2. 121 + let result = child_answer 122 + .value 123 + .with_rounding_mode(RoundingMode::HalfUp) 124 + .round(0); 136 125 Ok(Number::new(result, child_answer.unit)) 137 126 } 138 127 Ceil => { 139 - let mut result = child_answer.value.quantize(d!(1)); 140 - let rounding_change = result - child_answer.value; 141 - if rounding_change.is_negative() { 142 - result += d!(1); 143 - } 128 + let result = child_answer.value.ceil(); 144 129 Ok(Number::new(result, child_answer.unit)) 145 130 } 146 131 Floor => { 147 - let mut result = child_answer.value.quantize(d!(1)); 148 - let rounding_change = result - child_answer.value; 149 - if !rounding_change.is_negative() { 150 - result -= d!(1); 151 - } 132 + let result = child_answer.value.floor(); 152 133 Ok(Number::new(result, child_answer.unit)) 153 134 } 154 135 Abs => { 155 - let mut result = child_answer.value.abs(); 156 - let rounding_change = result - child_answer.value; 157 - if rounding_change == d!(-0.5) { 158 - result += d!(1); 159 - } 136 + let result = child_answer.value.abs(); 160 137 Ok(Number::new(result, child_answer.unit)) 161 138 } 162 139 Sin => { ··· 307 284 #[test] 308 285 fn test_functions() { 309 286 assert_eq!(eval_num("cbrt(125)"), "5"); 287 + assert_eq!(eval_num("cbrt(2)"), "1.25992104989487316476721060727822835057"); 310 288 311 289 assert_eq!(eval_num("sqrt(25)"), "5"); 290 + assert_eq!(eval_num("sqrt(2)"), "1.41421356237309504880168872420969807857"); 312 291 313 292 assert_eq!(eval_num("log(100)"), "2"); 314 293 assert_eq!(eval_num("log(2)"), "0.301029995663981195213738894724493026768"); ··· 335 314 336 315 assert_eq!(eval_num("sin(2)"), "0.9092974268256816953960198659117448427"); 337 316 assert_eq!(eval_num("sin(-2)"), "-0.9092974268256816953960198659117448427"); 317 + 318 + assert_eq!(eval_num("cos(2)"), "-0.41614683654714238699756822950076218977"); 319 + assert_eq!(eval_num("cos(-2)"), "-0.41614683654714238699756822950076218977"); 320 + assert_eq!(eval_num("tan(2)"), "-2.18503986326151899164330610231368254343"); 338 321 } 339 322 }