Skip to content

Add new quantity configuration option to keep int dtype - #20273

Merged
taldcroft merged 17 commits into
astropy:mainfrom
taldcroft:quantity-config-preserve-int-to-float
Sep 3, 2026
Merged

taldcroft merged 17 commits into
astropy:mainfrom
taldcroft:quantity-config-preserve-int-to-float

Conversation

@taldcroft

@taldcroft taldcroft commented Aug 26, 2026 •

Copy link
Copy Markdown
Member

Summary

This PR allows controlling the conversion integers by Quantity via a new configuration
variable. With this in place, the behavior of QTable is updated and an issue reading
files with integer columns with units is fixed.

Controlling the conversion of integers by Quantity

Creating a Quantity from integer input without an explicit dtype upcasts the values
to float, which silently changes values that float64 cannot hold. A new
units.quantity_convert_int_to_float configuration item takes default, always or
never to control this. The behavior of Quantity on its own is unchanged, so this part
is backward compatible.

Integer columns with a unit keep their dtype in a QTable

An integer Column with a unit added to a QTable is converted to a Quantity, and
that conversion used to cast the values to float. It now keeps the integer dtype, so a
19-digit Gaia source_id survives instead of coming back as a different number. This is
an API change; setting the configuration item to always restores the previous behavior.

Reading files with integer columns that have units

Reading into a QTable goes through the same conversion, so an integer column with a
unit read from VOTable, FITS, ECSV, HDF5 or parquet now keeps its dtype, and a write
followed by a read returns the values that were written. This is an API change but is
really a bug fix: the previous behavior corrupted values that the file itself stored
exactly, with nothing to indicate that anything had been lost.

Fixes #17963.

AI disclosure

This PR includes AI-generated content using Claude Opus 5. I have carefully reviewed the
content and fully understand the changes.

  • I certify that I am human and take responsibility for the code and interactions
    with reviewers.

Details

Click to expand

Make the integer-to-float conversion in Quantity configurable, and have QTable and
table deserialization opt out of it

Putting an integer column that has a unit into a QTable converts it to a float
Quantity. For a column of identifiers or counts this silently changes the values: a
Gaia source_id is a 19-digit integer that float64 cannot represent, so the number
that comes out is not the number that went in, and nothing warns about it. The columns
this hits are exactly the ones that arrive with a unit attached from VOTable and FITS
files, which is how the issue was found.

>>> c = Column([2741100559643251862], unit="")
>>> QTable({"object_id": c})["object_id"][0]
<Quantity 2.74110056e+18>         # main: float64, a different number
<Quantity 2741100559643251862>    # this branch: int64, exact

Quantity.__new__ defaults to dtype=np.inexact, which upcasts integer input to float.
That default is right for a Quantity created on its own — most physical quantities are
continuous — but wrong for a table column, where the dtype was chosen by whoever wrote
the data. This PR adds a units.quantity_convert_int_to_float configuration item with
values default, always and never, and has QTable and the table deserialization
code request never while constructing a Quantity from a column. Setting the item to
always restores the previous behavior everywhere, including on files that already
contain integers.

Only integer input is affected. np.inexact upcasts only what is not already inexact, so
float32, float64 and complex columns keep their dtype whether or not this PR is
applied, and Quantity([1, 2], u.m) on its own still gives float64 as before.

1. Why a configuration item rather than a keyword argument

An earlier attempt (#20247) added a QTable.preserve_quantity_dtype table attribute.
That works for a QTable built in memory, but the attribute has to be threaded through
every entry point that builds one, and QTable.read(..., preserve_quantity_dtype=True)
is not possible because the unified I/O layer passes unknown keywords to the reader
rather than to the table class. It also did nothing for a column reconstructed from a
serialized Quantity, so an ECSV file written from such a table read back as float.

A configuration item is read wherever the conversion actually happens, so the same
decision applies to a table built in memory, a table read from a file, and a column
rebuilt from __serialized_columns__, with no new keyword arguments.

2. dtype=np.inexact was doing two jobs

Quantity.__new__ used np.inexact both as the signature default and as an explicit
value a caller can pass, and the two were indistinguishable. Once the configuration item
exists that matters: an explicit dtype=np.inexact is a request, documented and tested
by test_float_dtype_promotion as "the minimum precision is float64", and a
configuration item should not override it.

The default is now np._NotGiven, so the three cases are distinct:

dtype meaning configuration applies
not given infer, upcasting integers by default yes
np.inexact explicit request to upcast integers no
None or a real dtype explicit no

Angle, Distance and FunctionQuantity declare dtype=np.inexact in their own
signatures. Under the sentinel these read as a deliberate statement that the class is
inherently floating point — an integer angle is not meaningful — so they keep the
declaration and are unaffected by the configuration. Their signatures are unchanged by
this PR.

3. Implementation

astropy/units/quantity.py:

  • Conf.quantity_convert_int_to_float, a ConfigItem whose default value is the list
    ["default", "always", "never"], so the first is the default and the others are the
    only permitted alternatives.
  • _keep_integer_dtype(), a context manager resolving default to never and passing
    always and never through unchanged. This is what makes always a real escape
    hatch rather than a synonym for default.
  • In __new__, the branch that used to be float_default = dtype is np.inexact is now
    three-way. Note that dtype = None has to happen for both the sentinel and
    np.inexact regardless of the configuration: leaving np.inexact in place would pass
    it to np.array(), which resolves it to float64 and makes never silently do
    nothing.

astropy/table/table.py — QTable._convert_col_for_table wraps the Quantity
construction in _keep_integer_dtype().

astropy/table/serialize.py — _construct_mixins_from_columns wraps the reconstruction
loop in _keep_integer_dtype(). This is the one place shared by every format that uses
__serialized_columns__, so it covers ECSV, FITS, HDF5 and parquet together.
Reconstructing a serialized column should reproduce what was written rather than
re-deciding a dtype, which is arguably a fix in its own right.

Nothing else changed. Table is untouched, and a Column with a unit in a plain Table
is still a Column.

4. Effect on I/O, and on #20248

With only the Quantity and QTable changes, writing was fixed but reading was not: an
ECSV file correctly received datatype: int64 and was then cast straight back to float
on read. Every format that serializes Quantity columns failed identically, which is
what pointed at the single call site in serialize.py:

format Quantity+QTable only with the serialize.py change
ECSV float64 int64
FITS float64 int64
HDF5 float64 int64
parquet float64 int64
VOTable int64 int64

VOTable was already correct because it stores plain columns with units, so it goes
through QTable._convert_col_for_table. That also means #20248, which added VOTable
specific handling for this, is not needed: reading the VOTable from #17963 on this branch
gives int64 with the identifier exact, with no changes under astropy/io/votable/.

5. Backwards compatibility

Quantity itself is unchanged for existing code: with the shipped configuration,
Quantity(...) from integer input still gives float64, and explicit dtype arguments
behave as before. The signature default is now np._NoValue rather than
np.inexact, so the rendered signature reads dtype=<dtype not given>; code passing
dtype=np.inexact explicitly still gets the upcast, and now keeps it under any
configuration.

QTable changes behavior. An integer column with a unit becomes an integer Quantity
where it used to become a float one, whether the table is built in memory or read from a
file. Code that assumed such a column was float may need updating, and integer division
and overflow semantics now apply where floating point ones did. Setting
units.quantity_convert_int_to_float to always restores the previous behavior
everywhere, including when reading a file that contains integers.

Changelog fragments are docs/changes/units/20273.feature.rst and
docs/changes/table/20273.api.rst. Narrative documentation is updated in
docs/units/quantity.rst, where the section on dtype no longer describes np.inexact
as the default, and in docs/table/mixin_columns.rst, where an attention block advised
against giving integer columns a unit because they would be converted to float. There are
three docs/whatsnew/8.1.rst entries: the new configuration item, the QTable change,
and the effect on reading files.

@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for.

  • Do the proposed changes actually accomplish desired goals?
  • Do the proposed changes follow the Astropy coding guidelines?
  • Are tests added/updated as required? If so, do they follow the Astropy testing guidelines?
  • Are docs added/updated as required? If so, do they follow the Astropy documentation guidelines?
  • Is rebase and/or squash necessary? If so, please provide the author with appropriate instructions. Also see instructions for rebase and squash.
  • Did the CI pass? If no, are the failures related? If you need to run daily and weekly cron jobs as part of the PR, please apply the "Extra CI" label. Codestyle issues can be fixed by the bot.
  • Is a change log needed? If yes, did the change log check pass? If no, add the "no-changelog-entry-needed" label. If this is a manual backport, use the "skip-changelog-checks" label unless special changelog handling is necessary.
  • Is this a big PR that makes a "What's new?" entry worthwhile and if so, is (1) a "what's new" entry included in this PR and (2) the "whatsnew-needed" label applied?
  • At the time of adding the milestone, if the milestone set requires a backport to release branch(es), apply the appropriate "backport-X.Y.x" label(s) before merge.

@taldcroft

Copy link
Copy Markdown
Member Author

@mhvk - this is a first cut at the idea discussed at #20247 (comment). I think the key code changes look OK but I'd like your input on the overall approach before going much further. As before, the actual new code footprint is quite small and easy to understand.

I haven't scrutinized the rest of the changes / tests so don't worry about that for now.

@mhvk mhvk left a comment

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.

@taldcroft - I like it! On second thought, I also think the configuration item is the right place for it. I do think, though, that for developers downstream it will be useful to make _keep_integer_dtype() public.

Really, it is mostly about names... For the conf item, I can see the logic of your name, since it is the main effect, but Quantity does also convert object (and should, but does not, convert bool... oops...). How about conf.force_inexact_dtype=["always", "default", "never"]? Or maybe a bit differently, conf.dtype_constraint=["input", "default", "inexact"]?

Somewhat independently, I think _keep_integer_dtype is a bit misnamed, since it does not always do so. So, I would call it something like preserve_dtype_by_default.

@pllim pllim added this to the v8.1.0 milestone Aug 26, 2026
@taldcroft
taldcroft marked this pull request as ready for review August 26, 2026 14:06
@taldcroft

Copy link
Copy Markdown
Member Author

@mhvk - excellent, glad you are on board! In the meantime Claude and I have made this into a nominally-complete PR.

I will change the conf to force_inexact_dtype and rename the function to preserve_dtype_by_default and make it public. But I do need to step away now but if you find some time you can look at everything now assuming those changes.

@taldcroft taldcroft added whatsnew-needed API change PRs and issues that change an existing API, possibly requiring a deprecation period labels Aug 26, 2026
@taldcroft

Copy link
Copy Markdown
Member Author

I updated the description as well.

@mhvk mhvk left a comment

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.

It's no big deal, so maybe more for future PRs, but I think assert_array_equal(a, b, [strict=True]) is nicer since if things go wrong, the test will tell you where they were wrong. With strict=True, you also get a dtype check.

My only bigger comment is that we need to refer to the name correctly in the changelog and whatsnew. (There is actually something slightly off with how we define conf in a level at which one presumably should never import...)

I still think it makes some sense to also expose the preserve_dtype_by_default context manager. That implies that one should be added to __all__, so that it becomes with u.preserve_dtype_by_default():

Comment thread astropy/table/tests/test_table.py Resolved
Comment thread astropy/table/tests/test_table.py Resolved
Comment thread astropy/table/tests/test_table.py Outdated
if expected_kind == "i":
# Only an integer dtype can hold these values exactly.
mask = getattr(col, "mask", np.zeros(len(col), dtype=bool))
vals = np.asarray(getattr(col, "unmasked", col).value)

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.

Why the asarray here?

assert u.Quantity(self.VALS, u.ct, dtype=float).dtype.kind == "f"
assert u.Quantity([1.5, 2.5], u.ct, dtype=int).dtype.kind == "i"
assert u.Quantity(self.VALS, u.ct, dtype=None).dtype == np.int64
# ``np.inexact`` is an explicit request to upcast, distinct from

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.

Nice catch for backward compatibility.

def test_inherently_float_subclasses(self, cls, unit, value):
"""Subclasses that declare ``dtype=np.inexact`` stay floating point.

An angle or a distance is inherently a floating point quantity, so these

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.

Interesting to here indirectly depend on the unit. I wonder if that will still give pushback, but I do agree it is more logical to have a class have different behaviour than for Quantity behaviour to depend on the unit.

Comment thread astropy/units/quantity.py Outdated
conf = Conf()


class _DtypeNotGiven:

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.

I'd tend to use np._NoValue for this. Slightly less explicit but a standard we can use that very much has the same purpose.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Great idea I didn't know about that.

Comment thread docs/changes/table/20273.api.rst Outdated
through a write/read round trip for the formats that serialize ``Quantity`` columns, such
as ECSV, FITS, HDF5 and parquet.

To restore the previous behavior, set the new ``units.quantity_convert_int_to_float``

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.

The link is not quite right: units.quantity.conf.<name> (same elsewhere)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed.

Comment thread docs/changes/units/20273.feature.rst Resolved
Comment thread docs/units/quantity.rst Outdated
Controlling the Conversion of Integers
--------------------------------------

Whether integer input is upcast is set by the ``quantity_convert_int_to_float``

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.

This should become a link to sphinx docs.

@taldcroft

Copy link
Copy Markdown
Member Author

@mhvk - thanks for the excellent review. I think I implemented all your suggestions with one commit each.

The sphinx change ended up making a new subsection in the API docs. The links all use the fully-qualified astropy.units.quantity.conf.quantity_convert_int_to_float name since the link reference is to the Conf class attribute and the lower-case conf is just a convention. I'm glad to have Claude doing battle with sphinx now instead of me.

@taldcroft
taldcroft requested a review from mhvk September 1, 2026 17:55

@mhvk mhvk left a comment

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.

Just a small bit for the docs.

Bit larger: before we had agreed to "change the conf to force_inexact_dtype". This still seems a bit more symmetric, but perhaps what you have is more explicit.

Since this affects Quantity behaviour generally, let me also ping @nstarman (though I suspect he will feel this change is long overdue!).

Comment thread docs/table/mixin_columns.rst Outdated
a count does not silently lose precision. This is controlled by the
:ref:`quantity_convert_int_to_float <quantity-convert-int-to-float>`
configuration item; setting it to ``always`` gives the conversion to ``float``
of astropy 7.x and earlier.

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.

This should be 8.0 or earlier, right? (may be somewhere else too).

Comment thread docs/units/quantity.rst Outdated

``always``
Integer input is upcast to float and those overrides are ignored. This gives
the behavior of astropy 7.x and earlier, in which an integer column with a

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.

Here is another one.

@nstarman
nstarman self-requested a review September 2, 2026 12:57
@nstarman

nstarman commented Sep 2, 2026 •

Copy link
Copy Markdown
Member

I need to review this PR, but first reaction in reading this is wondering why we want this configurable and not just following numpy dtype rules for the value — ie always keeping int-dtype inputs as int-dtype. IMO astropy over-promotes ints to floats since numpy handles upcasting automatically in math operations. Wouldn't it be easier (both in code and for users to understand) and more performant to not have to check configurations on every Q init and just let numpy do its thing?

@taldcroft

Copy link
Copy Markdown
Member Author

Bit larger: before we had agreed to "change the conf to force_inexact_dtype". This still seems a bit more symmetric, but perhaps what you have is more explicit.

I forgot having agreed to that. 😄

I guess my argument for keeping it quantity_convert_int_to_float is that I think it does a better job conveying the real issue at hand for typical users.

force_inexact_dtype is technically more correct, but I think most users that encounter it in code won't have a clue what it means. Interpreting those words requires first knowing what an "inexact" dtype is, and most casual users likely do not. Then it requires context that np.inexact is the default in the Quantity init.

In the end of course I'll defer to whatever you and @nstarman decide.

@mhvk

mhvk commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

@taldcroft - I'm OK with keeping your choice, I agree it is more descriptive.

@nstarman - To me, the present solution is a good compromise, as I think Quantity should really be float by default. The only real exception is countable things, which are rare. Indeed, we already went that route with your solution of having a default dtype=np.inexact; the present PR is just allowing one to effectively set what the default is, as this is really needed for reading data (it has been a regularly occurring request...).

Note that in the present PR, the behaviour of converting to float is preserved for subclasses such as Angle. Indeed, for Longitude, which does in-place wrapping, integers would lead to breakage, and we really do not want to have integer angles in SkyCoord. Overall, I think supporting integers properly would be a very big hassle and even then leave open ample room for shooting oneself in the foot.

Anyway, with this PR one can now choose to always preserve integers, without it being forced on everyone (and one can choose to stick with how things were).

@nstarman

nstarman commented Sep 2, 2026 •

Copy link
Copy Markdown
Member

we already went that route with your solution of having a default dtype=np.inexact

That was more to enable float16 vs float64. I do like that this PR allows the dtype=None to not force inexact dtypes.

Anyway, with this PR one can now choose to always preserve integers, without it being forced on everyone (and one can choose to stick with how things were)

Nice!

I guess a speed test should make me happy :)

Indeed, for Longitude, which does in-place wrapping, integers would lead to breakage.

Wait, why? Exact values should always be safer than inexact ones near branch cuts (assuming you use dtype-upcasting for division) since you can't machine-precision your way into the wrong branch. E.g. log(0) vs log(-1e-16).


Not for this PR, and thus maybe not for this thread.
But what if we did something like numpy's array() vs ndarray, where there's Quantity(exact arguments and no input munging) and quantity(our flexible constructor options) ? With stuff like this PR I'm aperiodically worried about how complex Q's constructor is — and it's currently unavoidable since it's __init__. We fast-path a lot, but separating direct construction from complex parser has to be faster, particularly in such a hot-path operation.

Relatedly, I asked claude to speed-test matching numpy dtype-semantics vs our current setup. Unsurprisingly, it's faster by 0 to 2,100%, depending on the situation.
QUANTITY_INT_DTYPE_REPORT.md

@taldcroft

taldcroft commented Sep 2, 2026 •

Copy link
Copy Markdown
Member Author

@nstarman - good catch on asking for performance! The earlier version was introducing up to a 2.6x slowdown for common cases because Config items are really slow (I know that but forgot). As always Claude gets long-winded, but here is the performance now. The fix ended up changing the logic a fair bit, but really for the better because it is faster than main now!

PR #20273 — performance check

Default path only: no quantity_convert_int_to_float configuration item set and no preserve_dtype_by_default() context manager, i.e. what existing code that knows nothing about this PR will actually run.

main = 5bf73b31d3, branch = 455d9d73a5. Times are µs per call; the branch column is relative to main, so lower is better and 1.00x is parity.

Quantity.__new__, no dtype given — the changed path

case main (µs) branch
u.Quantity(q), scalar 0.654 0.93x
u.Quantity(q), 1000 elements 0.867 0.92x
u.Quantity(5.0, u.m) 0.909 0.94x
u.Quantity([1., 2., 3.]) 1.135 0.97x
u.Quantity(float_arr), 1000 elements 0.950 0.97x
u.Quantity([1, 2, 3]) — int 1.337 1.89x
u.Quantity(int_arr), 1000 — int 1.520 1.77x

Quantity.__new__, explicit dtype

case main (µs) branch
u.Quantity(int_arr, dtype=float) 1.245 1.01x

Common user idiom

case main (µs) branch
5.0 * u.m 1.373 0.97x
float_arr * u.m, 1000 elements 1.517 0.97x
int_arr * u.m, 1000 — int 2.078 1.61x

Control group — does not go through __new__

case main (µs) branch
q1 + q2, 1000 elements 3.104 0.99x
q.to(u.km), 1000 elements 2.309 1.00x
q[10] 0.891 1.01x

Table integration

case main (µs) branch
t['b'] = float_arr * u.m 10.499 1.00x
t['c'] = int_arr * u.ct — int 11.454 1.09x
QTable({'a': int_arr * u.ct}) — int 13.643 1.09x

Reading the table

Float and Quantity input — the overwhelming majority of real use — is at parity or slightly better. Integer input costs ~1.1 µs more per call, which is the one case where the configuration item genuinely has to be consulted. In relative terms that is large for small arrays (u.Quantity([1, 2, 3]), 1.89x) because the operation itself is only ~1.3 µs; it is amortised away for anything of real size.

The control group confirms the measurement is picking up Quantity.__new__ and not general noise: operations that never construct a Quantity through __new__ are unchanged.

Why integer input pays and float input does not

Reading a ConfigItem is not a cheap attribute access — ConfigItem.__get__ calls get_config() and then _validate_val() on every access:

conf.quantity_convert_int_to_float   1.10 µs
conf.latex_array_threshold           1.32 µs   (pre-existing item, same cost)
plain global read                    0.004 µs

An earlier revision of this PR read the item unconditionally at the top of __new__, ahead of every fast path, which cost a flat +1.1 µs on every Quantity creation without an explicit dtype — up to 2.60x on the smallest cases. The item is now read only after value.dtype.kind shows the input is integer or object and could therefore be upcast, so the float paths never touch it.

Method

timeit, autorange() to pick the loop count, then min of 7 repeats. Two full rounds per revision, interleaved with checkouts to control for machine drift; the reported figure is the min across rounds.

Environment: Python 3.13.2, numpy 2.2.3, macOS 26.5.2 arm64.

The branch is behind main, so main is not strictly the branch point. As a check, the merge base 7d0a9aa781 was benchmarked as well and agrees with main to within 0.96–1.03x on every case, so nothing landing between them confounds the comparison.

Comment thread astropy/units/quantity.py Outdated
Comment on lines +509 to +512
# Neither sentinel is a dtype numpy can use, so replace both with None,
# keeping the original to decide below whether integer input is upcast
# to float: np.inexact is an explicit request to upcast, np._NoValue
# means no dtype was given and it is up to the configuration.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I notice this comment is not great so I can fix it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed

Comment thread astropy/units/quantity.py Outdated
Comment on lines +624 to +629
dtype_arg is np.inexact
or (
dtype_arg is np._NoValue
and conf.quantity_convert_int_to_float != "never"
)
):

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I just noticed this repeated code. I can make this into a private helper function if you prefer.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I just did it.

@mhvk mhvk left a comment

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.

Yes, good catch indeed. I had also forgotten that what looks like a straight attribute access is in fact very slow. But calling it only when actually relevant solves that, great!

@taldcroft
taldcroft force-pushed the quantity-config-preserve-int-to-float branch from 474e61d to cb6de79 Compare September 3, 2026 14:02
@taldcroft
taldcroft enabled auto-merge (squash) September 3, 2026 14:02
@taldcroft
taldcroft merged commit 6a84a67 into astropy:main Sep 3, 2026
40 checks passed
@taldcroft
taldcroft deleted the quantity-config-preserve-int-to-float branch September 3, 2026 14:18
@taldcroft

Copy link
Copy Markdown
Member Author

@mhvk @nstarman - Thanks again for the thorough reviews, progress!!

@pllim pllim mentioned this pull request Sep 4, 2026
1 task
@pllim

pllim commented Sep 4, 2026

Copy link
Copy Markdown
Member

For the record, anything that touches dtype should probably run Extra CI in the future. Please see #20318

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

Labels

API change PRs and issues that change an existing API, possibly requiring a deprecation period table units whatsnew-needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: QTable converts int columns to floats

4 participants