Skip to content

Commit e8ab8e4

Browse files
committed
Implemented ceil/floor methods for QQbar
Added .ceil() and .floor() methods to AlgebraicNumber_base to handle algebraic numbers correctly. This fixes the bug reported in #39345 where ceil(QQbar(0)) would fail due to reliance on numerical interval arithmetic. The method will prioritize checking for exact integers algebraically before falling back to numerical methods for real, non-integer values. Also raise a TypeError for complex numbers with non-zero imaginary parts, consistent with the standard definition of ceil/floor. Added the doctests.
1 parent 8dab374 commit e8ab8e4

File tree

1 file changed

+93
-0
lines changed

1 file changed

+93
-0
lines changed

src/sage/rings/qqbar.py

+93
Original file line numberDiff line numberDiff line change
@@ -3789,6 +3789,99 @@ class AlgebraicNumber_base(sage.structure.element.FieldElement):
37893789
sage: AA(sqrt(65537)) # needs sage.symbolic
37903790
256.0019531175495?
37913791
"""
3792+
def ceil(self):
3793+
"""
3794+
Return the ceiling of self, the smallest integer >= self.
3795+
3796+
This method first check if the number is algebraically an integer.
3797+
If not, it checks if the number is real. If it is real (and not
3798+
an integer), it uses numerical interval methods to find the ceiling.
3799+
If the number is complex with a non-zero imaginary part, it raises
3800+
a TypeError, as ceiling is typically not defined for such numbers.
3801+
3802+
EXAMPLES::
3803+
3804+
sage: QQbar(37/10).ceil()
3805+
4
3806+
sage: QQbar(-37/10).ceil()
3807+
-3
3808+
sage: QQbar(4).ceil()
3809+
4
3810+
sage: QQbar(0).ceil()
3811+
0
3812+
sage: ceil(QQbar(sqrt(8)) - 2*QQbar(sqrt(2))) # Test case from #39345
3813+
0
3814+
sage: QQbar(sqrt(2)).ceil()
3815+
2
3816+
sage: QQbar(-sqrt(2)).ceil()
3817+
-1
3818+
sage: AA(sqrt(2)).ceil() # Works for AA too
3819+
2
3820+
3821+
sage: ceil(QQbar(3/2 + 10^(-100)*I)) # Testing complex near real
3822+
Traceback (most recent call last):
3823+
...
3824+
TypeError: ceil() not defined for complex numbers with non-zero imaginary part
3825+
"""
3826+
if self.is_integer():
3827+
return ZZ(self)
3828+
3829+
imag_part = self.imag()
3830+
if not imag_part.is_zero():
3831+
raise TypeError("ceil() not defined for complex numbers with non-zero imaginary part")
3832+
3833+
# If the number is real and confirmed not an integer, use the interval refinement method.
3834+
# We need to ensure we operate on an AlgebraicReal instance to call _floor_ceil.
3835+
# AA._floor_ceil is defined in the AlgebraicReal subclass.
3836+
real_self = AA(self)
3837+
return real_self._floor_ceil(lambda x: x.ceil())
3838+
3839+
def floor(self):
3840+
"""
3841+
Return the floor of self, the largest integer <= self.
3842+
3843+
This method first checks if the number is algebraically an integer.
3844+
If not, it checks if the number is real. If it is real (and not
3845+
an integer), it uses numerical interval methods to find the floor.
3846+
If the number is complex with a non-zero imaginary part, it raises
3847+
a TypeError, as floor is typically not defined for such numbers.
3848+
3849+
EXAMPLES::
3850+
3851+
sage: QQbar(37/10).floor()
3852+
3
3853+
sage: QQbar(-37/10).floor()
3854+
-4
3855+
sage: QQbar(4).floor()
3856+
4
3857+
sage: QQbar(0).floor()
3858+
0
3859+
sage: floor(QQbar(sqrt(8)) - 2*QQbar(sqrt(2))) # Test case from #39345
3860+
0
3861+
sage: QQbar(sqrt(2)).floor()
3862+
1
3863+
sage: QQbar(-sqrt(2)).floor()
3864+
-2
3865+
sage: AA(sqrt(2)).floor() # Works for AA too
3866+
1
3867+
3868+
sage: floor(QQbar(3/2 + 10^(-100)*I)) # Testing complex near real
3869+
Traceback (most recent call last):
3870+
...
3871+
TypeError: floor() not defined for complex numbers with non-zero imaginary part
3872+
"""
3873+
# Check for exact integer FIRST.
3874+
if self.is_integer():
3875+
return ZZ(self)
3876+
3877+
# Check if the number is real.
3878+
imag_part = self.imag()
3879+
if not imag_part.is_zero():
3880+
raise TypeError("floor() not defined for complex numbers with non-zero imaginary part")
3881+
3882+
# If it's real and confirmed not an integer, use the interval refinement method.
3883+
real_self = AA(self)
3884+
return real_self._floor_ceil(lambda x: x.floor())
37923885

37933886
def __init__(self, parent, x):
37943887
r"""

0 commit comments

Comments
 (0)