Skip to content

round(::Type{<:AbstractFloat}, x, ::RoundingMode) violates docstring #52355

@mikmoore

Description

@mikmoore

I don't have the development branch checked out right now, so forgive me for manually-interpreting this code and let me know if any of my concerns are due solely to my mistakes. This issue is in response to new functionality introduced in #50812, which has not been included in a release version (as of v1.9).

I take issue with the implementation

round(::Type{T}, x, r::RoundingMode) where T = convert(T, round(x, r))

and the docstrings related to rounding functions. For example, ?floor in #50812 states

floor(x) returns the nearest integral value of the same type as x that is less than or
equal to x.
floor(T, x) converts the result to type T, throwing an InexactError if the floored
value is not representable a T.

(note the minor typo "not representable a T", but that's not why I'm here)

However, under the above implementation we have floor(Float32, 0xffff_ffff) == 2f0^32 > 0xffff_ffff. The floor(0xffff_ffff) part is executed correctly (no-op because integer argument), but the subsequent convert(Float32, 0xffff_ffff) results in upward rounding and yields an invalid result (being larger than the input). By the current docstring, the result should be an InexactError since 0xffff_ffff is not representable as a Float32. Alternatively, one might suggest that the largest not-greater Float32 be returned (in this case prevfloat(2f0^32)) but that would require a docstring change.

Possible solutions include:

  • Change the docstring to remark that convert is naively applied to the result of floor(x), so that this behavior is explainable under the stated semantics.
  • Throw an InexactError, as the written docstring indicates should happen.
  • Remove the unrestricted Type argument from the rounding functions. Retain some special cases like Type{<:Integer} (which already exist in release versions) where such issues cannot arise.
    • On current release v1.9, floor(Float32, 0xffff_ffff) is a MethodError
    • Is floor(T, x) really that much better than T(floor(x)) or convert(T, floor(x))? I'd be happy to have it except that it's wrong here. At least with the latter two the return value is explainable via the rounding behavior of T conversion.
  • Adjust the implementation to return prevfloat(floor(T,x)) when floor(T,x) > x and T<:AbstractFloat, with similar changes to other rounding functions. Adjust the docstrings accordingly to accomodate this possibility. Something in the flavor of "return the largest integral-valued T that is less than or equal to x".
    • This might be somewhat expensive since Julia doesn't currently give control over hardware rounding modes (to get the correct result in the first place) and integer-float comparisons aren't cheap (to fix the result in post-processing).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIndicates an unexpected problem or unintended behaviorcorrectness bug ⚠Bugs that are likely to lead to incorrect results in user code without throwingmathsMathematical functions

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions