Coder Social home page Coder Social logo

paupino / rust-decimal Goto Github PK

View Code? Open in Web Editor NEW
912.0 912.0 169.0 170.67 MB

Decimal number implementation written in pure Rust suitable for financial and fixed-precision calculations.

Home Page: https://docs.rs/rust_decimal/

License: MIT License

Rust 99.95% Shell 0.05%
decimal financial-calculations number rust

rust-decimal's People

Contributors

0e4ef622 avatar anatols avatar andrewspeed avatar arkaitzj avatar barnardb avatar c410-f3r avatar edwardycl avatar hathawsh avatar hengchu avatar jean-airoldie avatar jekirl avatar jp-ellis avatar koivunej avatar konishchevdmitry avatar maplant avatar michaelmattig avatar michalsieron avatar mkatychev avatar nicksenger avatar nvzqz avatar paupino avatar pfrenssen avatar remkade avatar robjtede avatar schungx avatar smessmer avatar the-kenny avatar turion avatar vorot93 avatar xilec avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rust-decimal's Issues

Improve `from_scientific` performance

At the moment, the performance of this function has not been optimized. Essentially the format of scientific notation is similar to the internal representation of Decimal so we should be able to take advantage of this.

std::cmp::min() doesn't work as expected

the following assertions fails for me using version 0.7.0

assert_eq!(cmp::min(Decimal::from_str("-0.5").unwrap(), Decimal::from_str("-0.01").unwrap()), Decimal::from_str("-0.5").unwrap());

Multiplication underflow should be rounded instead of panicing

Currently, multiplication can underflow in certain situations from the following lines:

rust-decimal/src/decimal.rs

Lines 1809 to 1817 in 7a3728a

// We underflowed, we'll lose precision.
// For now we panic however perhaps in the future I could give the option to round
if final_scale > MAX_PRECISION {
panic!(
"Multiplication underflowed: {} > {}",
final_scale,
MAX_PRECISION
);
}

For consistency, we should attempt to round on underflow situations whilst applying checked functions for overflow.

Improve/validate micro-benchmarking results

At the moment, there is a lot of variance between runs which can be mitigated with some libraries such as criterion.rs. I want to make sure that an appropriate range of inputs is benchmarked as well as validate that the results are able to be accurately compared for improvements.

Incorrect results when converting and printing long numbers from strings.

println!("{}", Decimal::from_str("11.111111111111111111111111111").unwrap());
// Prints: 11.111111111111111111111111111 (Correct)
println!("{}", Decimal::from_str("11.11111111111111111111111111111").unwrap());
// Panics; scale exceeds max precision. That's fine. However...

println!("{}", Decimal::from_str("11.1111111111111111111111111115").unwrap());
// Prints: 3.1882948596846773517567160779 (Incorrect)
println!("{}", Decimal::from_str("115.111111111111111111111111111").unwrap());
// Prints: 35.882948596846773517567160775 (Incorrect)
println!("{}", Decimal::from_str("1115.11111111111111111111111111").unwrap());
// Prints: 322.82948596846773517567160775 (Incorrect)

Implement `abs` function

This should be relatively easily since we store the negative bit within the flag however would still be a useful function to have.

Unexpected Panics

Hi,

I understand that rust-decimal panics in case of unrecoverable errors. However, Today I encountered two panics that seem odd.

    let rate = Decimal::new(19, 2);
    let part = rate / (rate + Decimal::new(1, 0));
    let result = Decimal::new(169, 0) * part; // panics

This will panic with Multiplication underflowed. However if I switch the operands (to part * Decimal::new(169, 0)), it does not panic anymore.

I tried to also fix this issue by rounding the part variable first, but this panicked as well:

    let rate = Decimal::new(19, 2);
    let part = rate / (rate + Decimal::new(1, 0));
    let part = part.round_dp(2); // panics

It panics with Cannot have an exponent greater than 28 (29 > 28).

Thanks

implement std::ops::Neg for Decimal

the following code fails to compile:

let x = dec!(7314);
let y = -x;

with error:

40 |     let y = -x;
   |             ^^ cannot apply unary operator `-`
   |
   = note: an implementation of `std::ops::Neg` might be missing for `rust_decimal::Decimal`

do you want std::ops::Neg implemented? then i'd do so and open a PR : )

Error using csv/serde

CSV deserialize error: record 1 (line: 2, byte: 523): invalid type: floating point `17.19287`, expected a Decimal type representing a fixed-point number

It doesn't seem to be related to parsing, since Decimal::from_str("17.19287") works fine.

Postgres integration

I'm interested in ability of saving/loading rust Decimal in Postgres number and saw some implementation in this crate. But I have a list of questions.

What is current status of integration with Postgres? (ready to use or need some changes)
Is there any documentation about this feature?
Is there any restrictions of usage for current implementation?

Approximate FromPrimitive f32/f64 impls

Right now FromPrimitive is implemented with the default f32/f64 behavior of rounding to an int.... I would propose that there be a lossy implementation or that it always return None -- I much prefer the former.

In C# it is handled as follows:

When you convert float or double to decimal, the source value is converted to decimal representation and rounded to the nearest number after the 28th decimal place if required. Depending on the value of the source value, one of the following results may occur:

If the source value is too small to be represented as a decimal, the result becomes zero.

If the source value is NaN (not a number), infinity, or too large to be represented as a decimal, an OverflowException is thrown.

change Debug for Decimal to print represented number

currently Decimal derives Debug

#[derive(Clone, Copy, Debug)]

when printing out for a example a vec of some structs containing decimals with {:?} or {:#?}
in place of the decimals one gets a lot of Decimal { flags: 131072, hi: 0, lo: 3260, mid: 0 }
and has to do additional annoying work to print the actual number represented.

implementing Debug like Display and printing the number represented by the decimal would help me a lot.

is this something you want?

would be happy to implement this then!

Something ain't right with arithmetics here...Overflow?

I'm doing some math in my rust code and it looks like the scale is messed up:

((844.13000000 -  843.65000000) * 0.1818181818181818181818181818 ) + 843.65000000
              ^^^              ^^^                                ^^^
      0.48000000      0.0457142857142857142857142856     84369571428571428571.42857143

It looks like there is an overflow. Shouldn't this be a panic?

Support floats within serde deserialization

Commonly, amounts in JSON are sent unquoted, i.e.

{ "amount": 100.20 }

At the moment, this causes the deserialization to raise an error whereas it'd be convenient if it could also parse this format successfully.

Create Benchmark tests for common operations

There are a number of functions in the library that can be optimized, however before optimization it'd be nice to be able to measure performance (so that performance gains are measured).

Implement format instructions

The following code:

let a = Decimal::new(100, 2);
let b = Decimal::new(300, 2);
let c = a / b;
println!("{}", c);
println!("{:.2}", c);

produces the output:

0.3333333333333333333333333333
0.

This is because the implementation converts to a string first and applies the pad method. Decimal should implement further format instructions to support producing 2dp without requiring rounding first up.

Replace ToDecimal with Into and From?

I was having a quick look through your code, and I see you have a new trait called ToDecimal which simply converts integer types into a decimal. I was curious as to why you didn't opt for the Into and From traits from the standard library which do exactly that?

Would you like a pull request to remove the ToDecimal trait and replace it with the standard library equivalent?

Optimize mul operation

Current benchmarks on 2.3GHz i7, 16GB DDR3, macOS High Sierra:

test bench_decimal_mul ... bench:   1,560,175 ns/iter (+/- 379,456)

`round_dp` limited to 20dp

Currently round_dp limits to 20dp to assist in pow10 lookups however is an unnecessary arbritrary limit. This should be updated to include an overflow word instead of relying on default implementations.

Use of deserialize_any breaks deserialization with bincode

Currently the deserialize implementation calls the serde deserialize_any function, which breaks Decimal serialization with non-self describing formats like bincode [0].

When implementing Deserialize, you should avoid relying on Deserializer::deserialize_any unless you need to be told by the Deserializer what type is in the input. Know that relying on Deserializer::deserialize_any means your data type will be able to deserialize from self-describing formats only, ruling out Bincode and many others.

There are two possible easy fixes:

  • Replace deserialize_any with deserialize_str
  • Simply have Decimal derive Serialize, Deserialize

[0] https://serde.rs/impl-deserialize.html

i128/u128 based implementation

My small experiments shows that implementation Decimal on top of types i128/u128 should be simpler and more performant. In my rough reimplementation of add method, timings of bench add_negative_pi drops from 133 ns/iter to 41 ns/iter. As I know, i128/u128 types supported on all rust targets.
I suppose it enough reasons to consider reimplementation or alternative implementation of Decimal type on top of i128/u128.

Optimize add/sub operations

These operations should be the easiest to optimize.

Current benchmarks on 2.3GHz i7, 16GB DDR3, macOS High Sierra:

test bench_decimal_add ... bench:   1,408,588 ns/iter (+/- 219,523)
test bench_decimal_sub ... bench:   1,357,040 ns/iter (+/- 256,864)

Optimize div operation

The current implementation of div focused on correctness over performance therefore can use some optimization.

Current benchmarks on 2.3GHz i7, 16GB DDR3, macOS High Sierra:

test bench_decimal_div ... bench:  47,344,736 ns/iter (+/- 6,536,510)

Review error handling within Decimal

There are some areas where panic is warranted however there are others (e.g. rescale, from_bytes_le) that we could utilize Rust's error handling further. In addition, with libraries such as error-chain we can perhaps make the error handling experience richer with little overhead.

Stabalize the shorthand Decimal notation

Creating a new decimal object is rather verbose. It'd be nice to have a shorthand option to define a decimal such as: 12.4M. This could possibly be implemented via a build.rs file.

Separate tests into `tests/`, or a `test` submodule.

At the moment, there are a lot of tests at the end of each file. It should not be difficult to move these tests over into the tests/ directory, and that'll help keep the main source files nice and tidy. If for some reason a test can't be moved outside, it should be moved into a separate submodule (as opposed to being their own stand-alone functions).

Implement add_assign traits et al

The introduction of add_assign, mul_assign et al allows us to easily do an arithmetic operation and an assign in a single operation. e.g. x += 2.

Extend test suites

Extend the test suites so that the tests are more dynamic. e.g. loop through an array of examples + results asserting along the way.

As part of this I want to also implicitly test transitive operations as appropriate. e.g. a + b = b + a, a * b = b * a.

Panic during comparison

The following test cases will fail with Decimal Overflow due to rescaling

    #[test]
    fn test_max_compares() {
        let x = "225.33543601344182".parse::<Decimal>().unwrap();
        let y = Decimal::max_value();
        assert!(x < y);
        assert!(y > x);
        assert!(y != x);

    }
    #[test]
    fn test_min_compares() {
        let x = "225.33543601344182".parse::<Decimal>().unwrap();
        let y = Decimal::min_value();
        assert!(x > y);
        assert!(y < x);
        assert!(y != x);
    }

missing panic on overflow !?

thanks for your work on this crate : ) so far it's been very useful to me.

i've replaced some code that previously used f64s by Decimals.
the code did some plotting in the end.
when comparing the plots i noticed a lot of differences.
i was able to reduce it to a minimal example:

    let mut x = dec!(7314.6229858868828353570724702);
    let y = dec!(1000);
    x += y;
    println!("{}", x);

prints:

391.8067344604490760026774366

this seems pretty bad to me : /

i guess it's an overflow. i think it should absolutely panic in this case instead of silently breaking math!

using rust decimal 0.8.1 and rustc 1.27.0-nightly (f0fdaba04 2018-05-15)

Implement `floor` and `ceil` functions

We currently support a round function - it's natural to also extend this to also support floor (Returns the largest integer less than or equal to a number) and ceil (Returns the smallest integer greater than or equal to a number) functions.

Implement `checked` operator functions

To help alleviate some of the panic scenario's currently present in the library we should introduce checked methods. Checked returns an Option<Decimal> and returns None on overflow.

Rescaling can overflow a number during standard operations

Currently, rescaling can cause a silent overflow during some common operations which leads to incorrect results. A good test case as raised by @rkusa is:

---- it_adds_decimals stdout ----
	thread 'it_adds_decimals' panicked at 'assertion failed: `(left == right)`
  left: `"12.45378151260504201681"`,
 right: `"4.5309652611786082574523276562"`: 11.815126050420168067226890757 + 0.6386554621848739495798319328', tests/decimal_tests.rs:168:8

In this particular case, 11.815126050420168067226890757 (scale 27) is being rescaled to a representation of 28. This causes an expected overflow logically however because the overflow is consequently ignored it leads to incorrect results. Behind the scene's it currently loses the significance and converts 11.815126050420168067226890757 to 3.8923097989937343078724957234. Consequently, 0.6386554621848739495798319328 + 3.8923097989937343078724957234 = 4.5309652611786082574523276562.

In order to resolve this; the rescale logic should be refactored and instead return an extra 32 bit's for overflow, which is ultimately used in any resulting calculations (as required).

Panic when formatting precision on fractional number.

Version: 0.7.2 (c22883b)

Decimal panics when formatting precision on a fractional number between 1 > n > -1 which contains leading zeros.

let foo: Decimal = "0.0023554701772169".parse().unwrap();
println!("{:.4}", foo);

thread 'main' panicked at 'attempt to subtract with overflow', /home/terry/.cargo/git/checkouts/rust-decimal-5e2779474ac0f95f/c22883b/src/decimal.rs:1658:30

The expected output is:

0.0023

Plans

Have you any plans for stabilization or moving forward in short term for this crate?

Add Continuous Integration

Continuous integration should be added to test the crate on Rust stable, beta and nightly.

Along with this, it will possible to have the master branch be protected so that only changes which pass the CI can be merged, and then the main development will happen on the develop branch (where things might break temporarily).

Improve postgres perfomance by precalculating groups

Currently we precalculate constants based on the number of groups that a postgres type has. We do this at runtime upon read. Each runtime group is 10000x the previous group.

We could comfortably pre-calculate this set of constants since they are the same between reads which should improve read performance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.