Move from float to Decimal for financial calculations#187
Draft
mbronk wants to merge 4 commits intoRustInFinance:mainfrom
Draft
Move from float to Decimal for financial calculations#187mbronk wants to merge 4 commits intoRustInFinance:mainfrom
mbronk wants to merge 4 commits intoRustInFinance:mainfrom
Conversation
Polish tax law requires different rounding methods depending on income type,
which was not previously implemented. This change brings the calculation into
compliance with Art. 63 Ordynacji Podatkowej (OP).
1. Per currency-converted "tax event" (i.e. stock sales, dividend...),
convert each result to full "grosz" (0,01 precision) - before
summing.
Basis: Mathematical rounding rules (0,01zł is the lowest monetary
value)
2. Interests and dividends are separated as they follow different
rounding rules when calculating the lump-sum tax
a) Interests aggregate (art. 30a ust. 1 pkt 3 PIT) as well as
their resulting lump-sum tax, are rounded UP to the nearest
full "grosz" — art. 63 §1a OP
b) Dividends aggregate (art. 30a ust. 1 pkt 4 PIT) as well
as their resulting lump-sum tax, are rounded to the nearest
full ZLOTY (0,50zl -> 1zl) — art. 63 §1 OP
c) Foreign tax withholding: no rounding the standard FX rule
to round to grosz (0,01) precision - rule #1 (above)
d) Net/gross/cost stock proceeds are not subject to lump-sum
tax calculations and reported in full on PIT-38 form, hence
only standard FX rules (#1-above) applies and they are reported
with "grosz" precision.
Signed-off-by: Mateusz Bronk <mbronk@users.noreply.github.com>
Per tax advisor consultation with Naczelna Izba Skarbowa and Art. 63 Ordynacja Podatkowa, rounding should only be applied to final tax base and tax amount sums, not to individual transactions. Change default behavior to carry full f32 precision through per-transaction FX conversions. The previous per-transaction rounding to grosz is preserved as an opt-in flag: - CLI: --round-per-transaction - GUI: Options menu toggle (MenuBar) Also add default-run to Cargo.toml to resolve binary ambiguity. Signed-off-by: Mateusz Bronk <mbronk@users.noreply.github.com>
Signed-off-by: Mateusz Bronk <mbronk@users.noreply.github.com>
Replace all f32/f64 money types with rust_decimal::Decimal for precise financial arithmetic. This eliminates floating-point representation errors in tax computations. Motivation: starting at 131,072 PLN (2^17), f32's ULP reaches 0.015625, exceeding 0.01 PLN and making it impossible to represent individual grosz values correctly. Any realistic portfolio of ~$30k+ in US stock sales converted at ~4 PLN/USD crosses this threshold. A new test (test_decimal_precision_vs_f32_sold_taxation) demonstrates a concrete 1-grosz error on a PIT-38-realistic scenario with just 3 trades. Key changes: - Add rust_decimal 1.36.0 (macros, serde-with-float features) - Convert all money types (Currency, Transaction, SoldTransaction, TaxCalculationResult) and rounding functions to Decimal - Update parsers: PDF uses DecimalEntry; CSV parses &str→Decimal directly; XLSX converts calamine f64→Decimal (IEEE 754 unavoidable) - NBP rates: serde-with-float deserialization, cached rates as dec!() - ECB rates: Decimal division for inverse exchange rates - Round gross_sold/cost_sold to grosz in run_taxation output - Add rust_decimal.natvis for VS debugger visualization - Add proof test for f32 grosz error; update all existing tests Signed-off-by: Mateusz Bronk <mbronk@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Important
Non-standalone!1 (therefore
DRAFT, until #181 is dispositioned)PLNamount rounding for compliance with PL tax rules 💰 #181.Summary
Replace all
f32/f64money 💰 types withrust_decimal::Decimalfor precise financial arithmetic.This eliminates floating-point representation errors in tax computations.
Motivation:
Starting at
131,072PLN (2^17),f32's ULP reaches0.015625, exceeding0.01 PLNand making it impossible to represent individual grosz values correctly. Any realistic portfolio of~$30k+in US stock sales converted at~4 PLN/USDcrosses this threshold.test_decimal_precision_vs_f32_sold_taxation) demonstrates a concrete 1-grosz error on a PIT-38-realistic scenario with just 3 trades.grossvalues).Key changes:
rust_decimal1.36.0(macros,serde-with-floatfeatures)Currency,Transaction,SoldTransaction,TaxCalculationResult) and rounding functions toDecimalDecimalEntry; CSV parses&str→Decimaldirectly; XLSX converts
calamine f64→Decimal(IEEE 754 unavoidable)serde-with-floatdeserialization, cached rates asdec!()Decimaldivision for inverse exchange ratesgross_sold/cost_soldto grosz inrun_taxation outputrust_decimal.natvisfor VS debugger visualizationf32grosz error; update all existing testsFootnotes
Change itself is standalone, but can't commit time to revolving merge conflicts in all possible merge order permutations, therefore submitting as-is, the way I use these changes on my forked vsn. ↩