-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Promotion rules #49
Comments
I'll give a simplied example that I bumped into... I am working on a financial application using cost = Fixed{Int,2}(5)
sales_tax_rate = .05
sales_tax = cost*sales_tax_rate
total = cost + sale_tax 💥 I was expecting a To fix it, I'd need to do something like cost = Fixed{Int,2}(5)
sales_tax_rate = .05
sales_tax = FixedDecimal{Int,2}(cost*sales_tax_rate)
total = cost + sale_tax Not the end of the world, but it feels a little clumsy to me. |
Over on Slack, someone (I'll edit his name back here if he gives permission) pointed out that
This makes sense and explains how Int/FixedDecimal -> FixedDecimal, FixedDecimal/Int -> FixedDecimal and FixedDecimal/Float -> Float is more consistent than it seemed to me at first. So maybe it is ok 🤔 |
Maybe it is not so clear cut? If "promotiness" score was like FixedDecimal/FixedDecimal -> Float but we have julia> one = FixedDecimal{Int,2}(1)
FixedDecimal{Int64,2}(1.00)
julia> three = 3one
FixedDecimal{Int64,2}(3.00)
julia> one/three
FixedDecimal{Int64,2}(0.33) I am still kind of leaning to having Edit: It would make some sense to me if 1/three === one/3 === one/3.0 === one/three The question is, should these all be Edit^2: Looks like I am addressing this comment 😊 https://github.com/JuliaMath/FixedPointDecimals.jl/blob/master/src/FixedPointDecimals.jl#L333-L334 # TODO: decide if these are the right semantics;
# right now we pick the bigger int type and the bigger decimal point |
Hi @NHDaly 👋 I just hopped onto the latest branch of your outstanding PR and see the following: julia> using FixedPointDecimals
julia> one = FixedDecimal{Int,2}(1)
FixedDecimal{Int64,2}(1.00)
julia> three = 3one
FixedDecimal{Int64,2}(3.00)
julia> 1/three
FixedDecimal{Int64,2}(0.33)
julia> 1.0/three
0.3333333333333333
julia> one/3
FixedDecimal{Int64,2}(0.33)
julia> one/3.0
0.3333333333333333
julia> one/three
FixedDecimal{Int64,2}(0.33)
julia> div(one,three)
FixedDecimal{Int64,2}(0.00)
julia> 1+three
FixedDecimal{Int64,2}(4.00)
julia> 1.0+three
4.0
julia> one+three
FixedDecimal{Int64,2}(4.00) If this is an intentional design choice that has been discussed / vetted / agreed, then no worries. I can live with it. However, https://github.com/JuliaMath/FixedPointDecimals.jl/blob/master/src/FixedPointDecimals.jl#L333-L334 does make it sound like it is still open for discussion. There is a case that could be made though that operations involving Edit: The way it currently is still feels inconsistent to me. We either widen or we don't widen. It seems a little mixed up right now. Here is what I would do (two options): Option 1: Math operations widen to
|
Hey @EricForgy, I'm glad you pointed this out! I agree it seems like probably surprising behavior, but I'm not too sure I agree with your feelings that we should think this out carefully. Thanks for raising the discussion. I wonder why the promotiness of FixedDecimals is less than Floats? I feel like both types are used to represent fractional numbers, so I could see that argument going either way. I guess floats can represent more numbers, but at the cost of precision of course. And presumably users of FixedPointDecimals are here for the precision (for usually financial applications I think). Since the FixedPointDecimal is a heavier-weight type, and the conversions are expensive, I think I would lean towards making it a more promoty type than Floats. I'm interested to hear what others think though! :) (I'm on my phone now but I'll think more about this tomorrow!) |
Hi @NHDaly 👋 Thanks for your comment 🙏 It sounds like we are tilting the same direction and I agree it would be good to hear the opinion of some others. Option 1 above puts promotiness of Option 2 above puts promotiness of Mathematically speaking, Option 1 feels right since a However, this is a case where I think real-world use cases might trump mathematical correctness. In my case with financial applications, I kind of expect cost = Fixed{Int,2}(5)
sales_tax_rate = .05
sales_tax = cost*sales_tax_rate
total = cost + sale_tax work as expected. Then again, I'm not sure which option would be fastest. We might need to benchmark each one to see and I would go with whichever is faster because I already take care of this in my package. I have a cost = Position(FI.USD,5)
sales_tax_rate = .05
sales_tax = cost*sales_tax_rate
total = cost + sale_tax already works the way I want it to, but there may be some unnecessary conversion going on behind the scenes because of what is happening with |
Sorry for the delay @EricForgy. Thanks again for bringing this up. Just adding some more thoughts:
I'm not sure if this design choice has been discussed / vetted / agreed. And if it has been, it's been a while! 😋 It's probably worth pointing out, though, that the comment you're specifically referencing there is, I think, instead about what to do if you multiply But I agree with you that the comment implies that maybe a lot of this hasn't been too firmly set in stone. Another point to consider is ÷. I changed this in #40 to return an FD, but before it was returning an Int. That seemed weird to me, but now I'm less sure. Maybe it is better to stay consistent with Rational?: julia> FixedDecimal{Int64,4}(3) ÷ 2.0
1.0
julia> 3//2 ÷ 2//2
1 Now it will be more in line with the Int < FD < Float promotiness, i think, but worth considering for how we move forward. (And regarding performance, in this case converting back to an FD actually is slightly slower, so maybe making that change was a mistake, I'm not sure. Maybe just picking the most performant path for everything is the best approach?) Anyway, I agree that it would be nice to have some clarity here. It currently seems like we aren't following any consistent rules, and at least identifying and following some rule would be good. Ideally, the simpler the rule the better. My vote would be for Option 2, everything promotes to an FD, but I'd be happy with anything that's easy to understand and self-consistent. |
Hi @NHDaly 👋 I had completely forgotten about this 😅
I have always thought of > help?> div
search: div divrem DivideError splitdrive code_native @code_native
div(x, y)
÷(x, y)
The quotient from Euclidean division. Computes x/y, truncated to an integer. indicates it should return an
That would be my vote as well. Do you know of anyone else using this package? It would be good to get their opinions. Actually, this seems obvious to say it, but what if we just completely mimick the promotiness of Edit: I dunno. Even after discovering the match with |
Intuitively, I would expect
one/3 === one/3.0
sinceStrictly speaking,
one/3
should probably also be aFloat64
, BUT...I think users of this package might reasonably expect both
and
Otherwise, there would need to be a lot of conversion in the code that would kind of defeat its purpose. For example, if you wanted the result of
one/3.0
to remain aFixedDecimal
, you'd need to do something likeI think we should either:
one/3
also aFloat64
soone/3 === one/3.0
orone/3.0
also aFixedDecimal
soone/3 === one/3.0
I kind of prefer the latter, but in any case it currently seems inconsistent.
What do you think?
The text was updated successfully, but these errors were encountered: