Skip to content

Add BigDecimal Crate#224

Closed
akubera wants to merge 22 commits into
rust-num:masterfrom
akubera:topic/big-decimal
Closed

Add BigDecimal Crate#224
akubera wants to merge 22 commits into
rust-num:masterfrom
akubera:topic/big-decimal

Conversation

@akubera
Copy link
Copy Markdown

@akubera akubera commented Sep 16, 2016

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.

@akubera akubera mentioned this pull request Sep 16, 2016
… Err Results. Empty string now returns error (instead of zero). Added tests to test bad strings
@cuviper
Copy link
Copy Markdown
Member

cuviper commented Sep 20, 2016

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 BigInt do the real work (actually BigUint working under that), including your enhancement for dealing with underscores.

@cuviper
Copy link
Copy Markdown
Member

cuviper commented Sep 20, 2016

I know you are leaning on BigInt and BigUint to some extent, but I think most of the char-parsing is out of place. I think it should be possible to only worry about '.' and the 'e' exponent here, and leave everything else to the lower layer.

@akubera
Copy link
Copy Markdown
Author

akubera commented Sep 20, 2016

Ok, I believe a well placed partition method can simplify things. I'll see what I can do.

@akubera
Copy link
Copy Markdown
Author

akubera commented Sep 21, 2016

Errr, partition isn't what I thought it was (python's str.partition). It's now a few split_at calls and special case handling.

Question: Should I support underscores?

… searches and splits instead of copying everything into a vector. Added new support for raising i32 parsing errors.
@akubera
Copy link
Copy Markdown
Author

akubera commented Sep 21, 2016

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.

@akubera
Copy link
Copy Markdown
Author

akubera commented Sep 21, 2016

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).

Copy link
Copy Markdown
Member

@cuviper cuviper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread Cargo.toml Outdated
bigdecimal = ["num-bigdecimal"]
complex = ["num-complex"]
rational = ["num-rational"]
default = ["bigint", "complex", "rational", "rustc-serialize"]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add it to the default features here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread bigdecimal/Cargo.toml Outdated
@@ -0,0 +1,39 @@
[package]
authors = ["The Rust Project Developers"]
description = "Big integer implementation for Rust"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to update this.

Comment thread bigdecimal/Cargo.toml Outdated

[dependencies.serde]
optional = true
version = "0.7.0"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at bigint for how we're now allowing either serde 0.7 or 0.8.

Comment thread bigdecimal/Cargo.toml Outdated

[dependencies.rand]
optional = true
version = "0.3.14"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you actually using rand?

Comment thread bigdecimal/src/lib.rs Outdated
@@ -0,0 +1,637 @@
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
Copy link
Copy Markdown
Member

@cuviper cuviper Sep 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's 2016. Not that we've been very diligent about keeping these updated, but at least start on the right foot. :)

Comment thread bigdecimal/src/lib.rs Outdated
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);
Copy link
Copy Markdown
Member

@cuviper cuviper Sep 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See, set_scale is weird so it seemed a clone was needed here, though it's currently cloning on its own.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Comment thread bigdecimal/src/lib.rs

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))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.)

Comment thread bigdecimal/src/lib.rs Outdated
Some(loc) => {
let (base, exp) = s.split_at(loc);
// slice after 1 to skip the 'e' char
(base, try!(i64::from_str(&exp[1..])))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(&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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough

Comment thread bigdecimal/src/lib.rs

/// Creates and initializes a BigDecimal.
#[inline]
fn from_str_radix(s: &str, radix: u32) -> Result<BigDecimal, ParseBigDecimalError> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little strange to support a radix on something called "decimal", but hey, that's what Num wants... :)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think I should just Err if radix != 10?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I think that's reasonable.

Comment thread bigdecimal/src/lib.rs
}
};

let scale = decimal_offset - exponent_value;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

@cuviper
Copy link
Copy Markdown
Member

cuviper commented Sep 21, 2016

On the underscore support, I'm OK with having that -- it just belongs in BigUint's parser. That can be a PR independent of this one, if you'd like a smaller task. :)

@akubera
Copy link
Copy Markdown
Author

akubera commented Sep 21, 2016

Thanks for the review. Most things not from_str_radix and eq were written by @iterion, so I don't know if there was, for example, an intention to use rand or not.

Is adding "bigdecimal" to the Makefile all that's needed for travis testing to work?

I'll revisit the project this weekend.

@hauleth
Copy link
Copy Markdown
Member

hauleth commented Sep 21, 2016

@akubera yep, that should be everything what you need.

@iterion
Copy link
Copy Markdown

iterion commented Sep 21, 2016

@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.

@cuviper
Copy link
Copy Markdown
Member

cuviper commented Sep 26, 2016 via email

@t-botz
Copy link
Copy Markdown

t-botz commented Dec 7, 2016

What is the status on that? Rust is definitely lacking an implementation of BigDecimal.
I would be keen to work on it, I am just wondering what would be required.

@akubera
Copy link
Copy Markdown
Author

akubera commented Dec 7, 2016

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.

@rpedela
Copy link
Copy Markdown

rpedela commented Dec 29, 2016

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 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
hacker mailing list: https://www.postgresql.org/list/pgsql-hackers/

@homu
Copy link
Copy Markdown
Contributor

homu commented Feb 10, 2017

☔ The latest upstream changes (presumably #263) made this pull request unmergeable. Please resolve the merge conflicts.

@rubdos
Copy link
Copy Markdown

rubdos commented Apr 4, 2017

☔️ 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?

@rubdos
Copy link
Copy Markdown

rubdos commented Apr 4, 2017

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.

IRC:

2017-04-04 11:25:22 rubdos Someone knows out of the top of their head whether postgresql considers NUMERIC 1.00000 == 1.0 ?
2017-04-04 11:25:37 johto yes

@rubdos rubdos mentioned this pull request Apr 4, 2017
10 tasks
@iterion
Copy link
Copy Markdown

iterion commented Apr 4, 2017

@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!

@akubera
Copy link
Copy Markdown
Author

akubera commented Apr 4, 2017

Yes, that is fine with me.

@cuviper
Copy link
Copy Markdown
Member

cuviper commented Apr 4, 2017

I think what might be best for this is to develop BigDecimal as a standalone project.

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 num stability very conservatively, which doesn't mesh well with a crate that may still need nascent development iteration.

So, how would you folks feel about starting a separate project for BigDecimal? I think the only thing you'd lose is getting a re-export from the base num crate, but IMO that's not such a big deal. Is there any reason you think this needs to be part of rust-num/num?

@akubera
Copy link
Copy Markdown
Author

akubera commented Apr 4, 2017

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:

  1. Let anyone interested (@rubdos) create their own repository and work on their own. Maybe rust-num/num keeps an issue open keeping track of the status of participating projects.
  2. A rust-num owner could create a rust-num/bigdecimal or rust-num/bigdecimal-nursery repository, with different implementations managed on different branches. Tradeoffs and designs can be discussed & implemented in this repo, and collectively people learn what works and what doesn't.

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.
Also, providing a standard testing+benchmark suite would be nice.

@iterion
Copy link
Copy Markdown

iterion commented Apr 4, 2017

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.

@rubdos
Copy link
Copy Markdown

rubdos commented Apr 4, 2017

It looks like Add is bugged; I'm getting invoices of a few trillion euro's :D

@rubdos
Copy link
Copy Markdown

rubdos commented Apr 4, 2017

Is there any reason you think this needs to be part of rust-num/num?

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 rubdos/big-decimal-rs instead of rust-num. Btw, the Diesel implementation is linked in somewhere here, and I must say that it's pretty neat.

@rubdos
Copy link
Copy Markdown

rubdos commented Apr 4, 2017

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?

@cuviper
Copy link
Copy Markdown
Member

cuviper commented Apr 4, 2017

I think you're putting too much emphasis on rust-num's "blessing" -- we're just a couple of people who volunteered to maintain num when it was booted from the standard library. Beyond that, there's no connection to rust-lang proper as far as having some sort of official sanctioning power. But maybe I underestimate the network effects, who knows.

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 rust-num/num, move it into rust-num/big-decimal, or just continue living on its own.

@rubdos if you really want to keep an open merge request here, I guess that's ok too.

@sgrif
Copy link
Copy Markdown

sgrif commented Apr 4, 2017

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 FromSql<Decimal> for f64

@akubera
Copy link
Copy Markdown
Author

akubera commented Apr 4, 2017

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).

@rubdos
Copy link
Copy Markdown

rubdos commented Apr 5, 2017

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?

@cuviper
Copy link
Copy Markdown
Member

cuviper commented Apr 5, 2017

I found a few existing crates too:

I'd take a close look at decimate then...

@rubdos
Copy link
Copy Markdown

rubdos commented Apr 6, 2017

It's probably worth noticing that code based on @iterion's work has been pushed to the bigdecimal crate, and is further developed at @akubera 's repo

@cuviper
Copy link
Copy Markdown
Member

cuviper commented Apr 6, 2017

Cool - happy hacking everyone!

remexre pushed a commit to remexre/num that referenced this pull request Jun 1, 2017
@cuviper
Copy link
Copy Markdown
Member

cuviper commented Oct 7, 2017

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants