Skip to content

feat: Improve support for Decimal DType#3377

Merged
dangotbanned merged 10 commits intomainfrom
feat/decimal-dtype
Jan 24, 2026
Merged

feat: Improve support for Decimal DType#3377
dangotbanned merged 10 commits intomainfrom
feat/decimal-dtype

Conversation

@FBruzzesi
Copy link
Member

@FBruzzesi FBruzzesi commented Dec 30, 2025

Description

Adds precision and scale parameters to Decimal dtype.

TODO: Decide on stable API

What type of PR is this? (check all applicable)

  • 💾 Refactor
  • ✨ Feature
  • 🐛 Bug Fix
  • 🔧 Optimization
  • 📝 Documentation
  • ✅ Test
  • 🐳 Other

Related issues

Checklist

  • Code follows style guide (ruff)
  • Tests added
  • Documented the changes

@FBruzzesi FBruzzesi added enhancement New feature or request dtypes labels Dec 30, 2025
@FBruzzesi
Copy link
Member Author

Regarding the stable API, how do we feel for this feature? I would say that keeping it backward compatible for v2 might be a stretch, but for v1 it would be ok. cc @MarcoGorelli

@FBruzzesi FBruzzesi marked this pull request as ready for review December 30, 2025 18:39
@MarcoGorelli
Copy link
Member

thanks for your pr!

i was under the impression that precision was going to be removed, but it looks like that didn't happen, i'll study pola-rs/polars#19784 and get back to this

@dangotbanned
Copy link
Member

thanks for your pr!

i was under the impression that precision was going to be removed, but it looks like that didn't happen, i'll study pola-rs/polars#19784 and get back to this

To save you a long read, there was nothing conclusive in that issue.
But the PR that closed it does have a summary that's more digestible

On the Python-side, all that's changed is precision=38 gets assigned if you don't provide one to Decimal.

There are a lot more changes on the Rust-side (who would've guessed that?)
It appears that precision is here to stay, but there are a lot of (positive?) behavioral changes which we may have a tough time trying to align between backends.

I'm not pitching we do/don't do that - just trying to summarize the old-fashioned way 😉

Copy link
Member

@MarcoGorelli MarcoGorelli left a comment

Choose a reason for hiding this comment

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

thanks @FBruzzesi !

Comment on lines 228 to 229
def __init__(self, precision: int | None = None, scale: int = 0) -> None:
self.precision = 38 if precision is None else precision
Copy link
Member

Choose a reason for hiding this comment

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

not sure what i'm missing but why not

precision: int = 38

?

Copy link
Member

Choose a reason for hiding this comment

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

I suppose if we got an old pl.Decimal, that could have a None already?

Otherwise - yeah if we can avoid introducing the contructor with a None - I'm on board 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

It's solely to mirror polars signature: polars.datatypes.Decimal and code

    def __init__(
        self,
        precision: int | None = None,
        scale: int = 0,
    ) -> None:
        if precision is None:
            precision = 38


        self.precision = precision
        self.scale = scale

@FBruzzesi
Copy link
Member Author

@MarcoGorelli thanks for reviewing this. I have a couple of questions that might need to be discussed before merging:

  1. Stable API - What's your take here?
  2. Polars Decimal allows for any value of precision and scale at datatype initialization, however it raises an InvalidOperationError at runtime if scale is greater than precision. See example below. I would argue that to standardize the error message, we could raise such error during initialization.

Example:

import polars as pl

dtype = pl.Decimal(2, 3)  # Ok so far
pl.Series([1.2]).cast(dtype)  
> InvalidOperationError: scale must be less than or equal to precision

@dangotbanned dangotbanned mentioned this pull request Jan 10, 2026
32 tasks
@dangotbanned
Copy link
Member

@FBruzzesi

RE: (#3377 (comment)), (#3377 (comment))
I thought Decimal was marked as unstable in narwhals, but it looks like it wasn't? 🤔

@MarcoGorelli did you mean to introduce it as stable in #1571?
polars didn't stablize until a month after pola-rs/polars#24542 in pola-rs/polars#25020

@dangotbanned
Copy link
Member

dangotbanned commented Jan 21, 2026

  1. Polars Decimal allows for any value of precision and scale at datatype initialization, however it raises an InvalidOperationError at runtime if scale is greater than precision.
    See example below. I would argue that to standardize the error message, we could raise such error during initialization.

@FBruzzesi I agree, let's do that

dangotbanned added a commit that referenced this pull request Jan 22, 2026
Spotted while reviewing #3377

Pretty sure there's more of these to be found
@dangotbanned
Copy link
Member

dangotbanned commented Jan 23, 2026

Regarding the stable API, how do we feel for this feature? I would say that keeping it backward compatible for v2 might be a stretch, but for v1 it would be ok

@FBruzzesi Initially I was thinking we should do the same thing for v1 and v2.
The only precedent we have is #2192, where the attributes were only added in main.

But the more I've read through your PR, I've started to think we might not need it?
AFAICT, for most backends the issue would have been that we change the native data types that we produce.
It looks like we didn't do that much:

  • Raised instead of producing a native data type
    • arrow, duckdb, pandas_like, polars, spark_like
  • Continues to not have native support
    • dask

Note

Only ibis could see a runtime change by using our new defaults

Have I understood all of this correctly?
If so, I would suggest adding (if needed) version-specific behavior for ibis-only, instead of an entire DType

@MarcoGorelli
Copy link
Member

@MarcoGorelli thanks for reviewing this. I have a couple of questions that might need to be discussed before merging:

  1. Stable API - What's your take here?

seems fine, it should be stable in polars now

  1. Polars Decimal allows for any value of precision and scale at datatype initialization, however it raises an InvalidOperationError at runtime if scale is greater than precision. See example below. I would argue that to standardize the error message, we could raise such error during initialization.

Example:

import polars as pl

dtype = pl.Decimal(2, 3)  # Ok so far
pl.Series([1.2]).cast(dtype)  
> InvalidOperationError: scale must be less than or equal to precision

Sure sounds good (tbh i wouldn't have worried about standardising it, but no objections as you've already done it)

@FBruzzesi
Copy link
Member Author

Thanks both for reviewing 🙏🏼

@dangotbanned as mentioned in discord, I did some digging in the ibis code and settled on f98decc

Let me know if you are happy with the state of this PR 😇

Copy link
Member

@dangotbanned dangotbanned left a comment

Choose a reason for hiding this comment

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

cat-ptain on a narwhal

@dangotbanned dangotbanned merged commit b97202e into main Jan 24, 2026
24 of 40 checks passed
@dangotbanned dangotbanned deleted the feat/decimal-dtype branch January 24, 2026 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dtypes enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Enh]: Improve support for Decimal dtype

3 participants