Fork me on GitHub
#clojurescript
<
2024-05-27
>
Drew Verlee20:05:53

What functions would allow for x + y, and x - y, where x and y start off as strings that are limited to two decimal points (e.g 1.01)? I started off assuming i should parse the x and y, as floats, then fix them to two decimals (anything more isn't allowed by the business logic) and then parse that into a float again (because .toFixed returns a string). That looks like this in cljs:

(js/parseFloat (.toFixed (js/parseFloat "1.101") 2))
;; => 1.1
From there i assumed i could safely add and subtract , but i was quickly proven wrong (js code):
> 12.8 + 1.3
14.100000000000001
To get around this, i assumed i could multiple the number by 100 then parse to an int then do the math, then divide by 100, but that also causes arithmetic errors (js code):
> 100 * 1.1
110.00000000000001
So Instead of trying to do anything besides parse a float, i used string manipulation to make things into integers and then piece them back together (cljs):
(let [[d r] (clojure.string/split (.toFixed (js/parseFloat "1.101") 2) #"\.")
      b (js/parseInt (str d r))
      s (str b)
      split-index (- (count s) 2)
      a (str (subs s 0 split-index) "." (subs s split-index))]
  {:before b ;; the result of parsing the users float
   :after a}) ;; after some addition or subtraction, turn the number back into something that looks like a float with two decimal points
;; => {:after "1.10", :before 110}
This seems to work on the first three examples i tested, but it feels hacky. However that hackyness could be that the users didn't need to input decimal values in the first place, as their is no sense of fractions in the problem (no division or multiplication) and instead the users should have just encoded the numbers using whole numbers (e.g 110).

Sam Ferrell21:05:09

there are no integers in Javascript, only Number, and those errors are floating point precision errors which will appear regardless of how you parse strings as long as they end up as Numbers. i think most people just truncate the unwanted precision, or use a big decimal library if the math is important to be correct

Sam Ferrell21:05:34

( well there is there is BigInt if you can represent your problem using that )

Drew Verlee21:05:34

Your saying, any two Js Numbers, even if their "Whole Numbers" {0,1,2...} when added can result in a number which isn't actually the result of adding them?

Sam Ferrell21:05:50

arithmetic with 2 whole numbers never has precision errors I don't think, but certainly arithmetic with a whole number and non-whole numbers can

Sam Ferrell21:05:57

basically you'll have to chop off everything but the first two digits after every operation, in your case, i think, as youve described it

p-himik21:05:44

I would model it with either integers or fractions. IIRC there was a library for CLJS that implemented fractions, or maybe it was a JS one. But it's also easy to implement it yourself if the number of operations that you need is small.

Drew Verlee21:05:16

I just to support addition really.

Sam Ferrell21:05:15

I'm not sure but I think you're safe truncating every time, seems like your problem only requires a fixed number of digits to support

Drew Verlee21:05:30

Yeah, in this case that feels correct. In the other, which deals with american currency, i'm going to do some division so it's going to get tricky in another way.

Sam Ferrell21:05:12

currency is outside of my expertise 😬

p-himik21:05:24

Truncating is unsafe, you should round.

cljs.user=> (* 100 0.57)
56.99999999999999

p-himik21:05:19

Also note that JS does have a separate int type that might be useful if you decide to go the ratio route - js/BigInt.

👍 1
Sam Ritchie04:06:17

We have ratios in #emmy , initially we wrapped https://github.com/infusion/Fraction.js/ but the current implementation is all cljs

👀 1
Sam Ritchie04:06:52

Though with currency it does feel like you’d be better off using integers for everything and dividing by 100 only when you need to display (so, calculate with pennies)

💯 1
Ludger Solbach09:06:28

Doing currency with floats or doubles is a big anti pattern.