Add BigDecimal Crate#224
Conversation
Div is currently broken, as it only returns the integer portion instead of a float as one might expect. Other traits are stubbed out to satisfy the Num trait.
…dded support for exponential form in from_str.
…xed implementation of eq() so BigDecimals with large scale differences are compared correctly
…c if exponent is not a valid integer.
… Err Results. Empty string now returns error (instead of zero). Added tests to test bad strings
|
I still need to sit down and do a full review of this, but since you're apparently tweaking the parsing right now -- it looks over-complicated to me. I would actually expect this to be a rather "dumb" wrapper -- just finding the decimal point if it exists and letting |
|
I know you are leaning on |
|
Ok, I believe a well placed |
|
Errr, Question: Should I support underscores? |
… searches and splits instead of copying everything into a vector. Added new support for raising i32 parsing errors.
…e BigInt parser implementation)
…the decimal_offset algorithm.
|
Ok, I re-read and see now that underscore support should be removed. That's been done, and the from_str_radix function I think is ready for review. There are lots of comments, I hope that's ok. |
|
In terms of the decimal point, I find it and (if found) push the characters before and after it into a String, and pass that to the BigInt. I think that requires a copy. I don't know if there's a better way around that (aside from a BigInt constructor from Iterator and chain the two slices). |
cuviper
left a comment
There was a problem hiding this comment.
OK, here are some more points for you to work on.
I'm especially concerned about the computation required in eq though. We should research/explore if there are other representations that would be more convenient.
| bigdecimal = ["num-bigdecimal"] | ||
| complex = ["num-complex"] | ||
| rational = ["num-rational"] | ||
| default = ["bigint", "complex", "rational", "rustc-serialize"] |
There was a problem hiding this comment.
You should add it to the default features here.
| @@ -0,0 +1,39 @@ | |||
| [package] | |||
| authors = ["The Rust Project Developers"] | |||
| description = "Big integer implementation for Rust" | |||
|
|
||
| [dependencies.serde] | ||
| optional = true | ||
| version = "0.7.0" |
There was a problem hiding this comment.
Take a look at bigint for how we're now allowing either serde 0.7 or 0.8.
|
|
||
| [dependencies.rand] | ||
| optional = true | ||
| version = "0.3.14" |
| @@ -0,0 +1,637 @@ | |||
| // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT | |||
There was a problem hiding this comment.
It's 2016. Not that we've been very diligent about keeping these updated, but at least start on the right foot. :)
| fn add(self, other: &BigDecimal) -> BigDecimal { | ||
| let scale = max(self.scale, other.scale); | ||
| let left = self.clone().set_scale(scale); | ||
| let right = other.clone().set_scale(scale); |
There was a problem hiding this comment.
See, set_scale is weird so it seemed a clone was needed here, though it's currently cloning on its own.
There was a problem hiding this comment.
The math equations would benefit from a single clone (or rather "rescale") and then an inplace operator on the new object. It looks like std::ops::AddAssign was marked stable in 1.8; which means I cannot use that, right? Is there a way to write two implementations based on rustc version?
|
|
||
| impl fmt::Display for BigDecimal { | ||
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
| f.pad_integral(true, "", &self.int_val.to_str_radix(10)) |
There was a problem hiding this comment.
This will need a decimal point, or trailing zeros, or perhaps just an e+123 sort of suffix.
(Hmm, the scale is the opposite of a normal exponent -- that's kind of awkward now that I notice it.)
| Some(loc) => { | ||
| let (base, exp) = s.split_at(loc); | ||
| // slice after 1 to skip the 'e' char | ||
| (base, try!(i64::from_str(&exp[1..]))) |
There was a problem hiding this comment.
We currently still support Rust 1.0, but split_at wasn't added/stabilized until 1.4.
But just s[..loc] and s[loc+1..] should be fine. (It's mutable slicing where you get into trouble.)
There was a problem hiding this comment.
(&s[..loc], &s[loc..]), the beauty of half open ranges.
libs people don't think bumping the rust version is breaking rust-lang/rfcs#1619 (comment) Every such thing is a judgment call though, of course.
There was a problem hiding this comment.
@bluss I think they're wrong about that, and the comment you linked acknowledges that this idea was the most contentious part of that RFC. But this is not the place to argue that.
|
|
||
| /// Creates and initializes a BigDecimal. | ||
| #[inline] | ||
| fn from_str_radix(s: &str, radix: u32) -> Result<BigDecimal, ParseBigDecimalError> { |
There was a problem hiding this comment.
It's a little strange to support a radix on something called "decimal", but hey, that's what Num wants... :)
There was a problem hiding this comment.
Do you think I should just Err if radix != 10?
There was a problem hiding this comment.
Sure, I think that's reasonable.
| } | ||
| }; | ||
|
|
||
| let scale = decimal_offset - exponent_value; |
There was a problem hiding this comment.
Hmm, actually on the radix point, this is wrong for radixes other than 10, and others can't be perfectly converted.
(e.g. "0.1" radix 3 is 1/3)
|
On the underscore support, I'm OK with having that -- it just belongs in |
|
Thanks for the review. Most things not Is adding "bigdecimal" to the Makefile all that's needed for travis testing to work? I'll revisit the project this weekend. |
|
@akubera yep, that should be everything what you need. |
|
@akubera no idea why I added rand, my guess was it was just a copy paste from somewhere else that got left in. Edit: believe the same is true for Serde, I was just copying some things from other files in the project that was boilerplate. |
…ption and updated serde version.
…igInt errors. Removed `split_at` usage in favor of slices.
|
I'm not sure what tangible benefit you'd get from AddAssign. When you have
a local object, you can just move it by value into Add and accept what
comes out. That is, "x += y" and "x = x + y" are not much different.
|
|
What is the status on that? Rust is definitely lacking an implementation of BigDecimal. |
|
It works insofar the tests for construction and arithmetic pass, but the implementation is not as efficient as it could/should be. As it stands, it uses a BigInt to store the numerical data, so we lose access to the BigDigits which could be used for optimization for, say, testing equality. The other thing to worry about is how to manage precision. Right now I limit decimal places in the division operator with an arbitrary number. The python decimal module (I almost typed 'crate', haha) uses thread-scoped context objects to manage such information; that's probably not the way to go for Rust. I haven't worked with/looked at any other such library, someone should throw ideas around. I don't know the industry standards for such a library: is a goal keeping track of significant figures? should (1.00 == 1.00000)? should there be a function that returns false in that case? Should it coupled to the num-rational crate? Should every object have their own "context" properties? What rules govern objects with different such properties? I'd need input from experts before moving forward, and I've been busy and haven't had time to work on this for couple months. Any input would be appreciated. |
I think it may be worth looking at the source for Postgres's "numeric" type. The PG community cares a lot about correctness and I am willing to bet they have tackled many of your non-Rust-specific questions. Github mirror: https://github.com/postgres/postgres |
|
☔ The latest upstream changes (presumably #263) made this pull request unmergeable. Please resolve the merge conflicts. |
I rebase the thing on master, should I make a new PR? |
IRC:
|
|
@rubdos yeah, a new PR is how we originally handled it when I ran out of time to work on this. So, 👍 from me. Would still love to see this merged as well as BigDecimal support for diesel, which is why I originally started this! |
|
Yes, that is fine with me. |
|
I think what might be best for this is to develop There's clearly a lot of interest in having this type, and I feel bad about dragging my feet. But at the same time, there are still design questions that I'm not terribly comfortable with, and don't have great ideas how to make it better. Yet we rust-num maintainers are the ones stuck with the maintenance bill if we merge this. Plus, we treat So, how would you folks feel about starting a separate project for |
|
Other than relieving people of the paradox of choice, I don't think it would need to be in num. I think it'd be fine to have a few competing implementations, and perhaps one day there would be one that is unarguably better than the others, and be blessed by the num developers and merged into the project. I certainly like having a "one-stop-shop" crate handling multiple similar needs, and I would trust that the maintainers of such crates have worked on ensuring quality and interoperability. I can think of two options:
2 is obviously more difficult, but would provide people who know what they're doing an easy-to-find place to comment, and would reduce reinvention of the wheel for anyone who wants to start something new. |
|
Overall, I'm cool with the idea of separating this out into its own package. Ultimately, if the implementation stabilizes then rust-num/num would be a great place for it. But, we're not there yet and it also assumes that a single standard can be agreed upon. That said, it seems other languages make that choice for you: ruby, python, etc. I like some form of @akubera's option 2, as it doesn't necessarily preclude competing standards while still making it easy for newcomers to find some sort of community agreed upon decimal implementation. I'm happy to volunteer to help maintain any project that is created as well. |
|
It looks like |
Yes. Obviously because rust-num is the place to be for everything number related, and I think avoiding separation would enhance potential cohesion of the Rust ecosystem. I'm totally okay with splitting off; the only remaining question will be whether Diesel would still merge something that pulls from |
|
You know what; I'll make the merge request for a split-off crate. You guys leave it open, review from now and then, and in a few months, when things stabilize, we merge again. In the meantime, I'll maintain a separate crate for this alone. Sounds like an idea? |
|
I think you're putting too much emphasis on rust-num's "blessing" -- we're just a couple of people who volunteered to maintain The separate project doesn't need to be treated as a competition, unless you folks disagree about something between yourselves. Just have someone open a personal repo and invite the others to collaborate. Later down the road, we can evaluate whether it makes sense to merge into @rubdos if you really want to keep an open merge request here, I guess that's ok too. |
|
Someone please release a crate somewhere so I have a type to target for the PG decimal type. I'm sick of telling people that I'm not going to implement |
|
I've made a bigdecimal repository maintaining the history of the subdirectory here: akubera/bigdecimal-rs, I assume @iterion is ok with that. I've ignored the 1.0.0 compatible commits, so I only know it's compatible with stable (1.16.0) and beyond. @rubdos, you're free to clone (i.e. not github fork but hard clone and push to your own repo) and use however you and the Desiel project wishes (I started cherry-picking before I realized you said you'd fork it). |
I'd rather work together then. Want to meet up on an IRC channel, or shall I open some issues on your repo? |
|
I found a few existing crates too:
I'd take a close look at |
|
It's probably worth noticing that code based on @iterion's work has been pushed to the |
|
Cool - happy hacking everyone! |
|
I'm trying to clean up old PRs. Since you all have moved to a separate crate, I think that it's not helpful to leave this open. Hope it's going well! |
Rebase of pull request #193. Addresses issue #8.
Implementation of BigDecimal, which comprises a BigInt and a u64 scale factor, allowing for storage of 2^64 digits.
Further implementation is required to set the default maximum precision (so numbers like 1/3 are not represented by 2^64 digits). The current default is 100.
Add, Sub, Mul implemented by @iterion, I reimplemented Div, Eq, FromStr.