crystal
crystal copied to clipboard
Floating-point manipulation operations should not allow arbitrary numbers
The functions Math.ilogb, logb, ldexp, scalbn, scalbln, frexp, and copysign are only meaningful when their arguments are already the matching floating-point types; it is meaningless to talk about the "exponent" of 42_i32, because it is very clearly a fixed-point number. Thus I propose that we deprecate the restriction-less overloads of these methods and remove them in 2.0. What happens afterwards is that any such calls with incompatible number types will be an error:
module Math
def ldexp(value : Float32, exp : Int32); end
def ldexp(value : Float64, exp : Int32); end
def ldexp(value : Float32, exp : Int); ldexp(value, exp.to_i32); end
def ldexp(value : Float64, exp : Int); ldexp(value, exp.to_i32); end
end
Math.ldexp(1, 5) # Error: ambiguous call, implicit cast of 1 matches all of Float32, Float64
Math.ldexp("1".to_i, 5) # Error: no overload matches 'Math.ldexp' with types Int32, Int32
Any necessary floating-point conversions, when desired, must then occur at the call site. (In this example, the exp parameter is not a floating-point value being manipulated, so it may still perform type conversions.)
For reference, these methods currently have overloads with unrestricted parameters implicitly casted to float:
https://github.com/crystal-lang/crystal/blob/dd40a2442fa186add8a82b74edb14a90aa1dae05/src/math/math.cr#L602-L604
I think this is a bug because calling the function Math.ldexp with a floating-point exponent will return a wrong result:
# Math.ldexp(value, exp) is equivalent to:
value * 2 ** exp
# With an integer exponent the results match:
pp! Math.ldexp(1.2, 4) # => 19.2
pp! 1.2 * 2 ** 4 # => 19.2
# But not with a floating-point exponent:
pp! Math.ldexp(1.2, 4.5) # => 19.2
pp! 1.2 * 2 ** 4.5 # => 27.152900397563425
Math.besselj(order, value) and Math.bessely(order, value) have the same problem:
pp! Math.besselj(4 , 1.2) # => 0.005022666277311586
pp! Math.besselj(4.5, 1.2) # => 0.005022666277311586
# Correct result from https://www.wolframalpha.com/input?i=besselj(4.5,+1.2)
# 0.00179578...
pp! Math.bessely(4 , 1.2) # => -16.68618734525732
pp! Math.bessely(4.5, 1.2) # => -16.68618734525732
# Correct result from https://www.wolframalpha.com/input?i=bessely(4.5,+1.2)
# -40.9739...