From 1ce8140633025702fdebd5171e21007bc03dd981 Mon Sep 17 00:00:00 2001 From: KlausC Date: Wed, 1 Nov 2023 16:12:57 +0100 Subject: [PATCH] Pack of tests and trivial fixes --- .gitignore | 1 + README.md | 86 ++++++++++++++++++-------------------- src/float/prearith.jl | 32 ++------------ src/libarb/ArbComplex.jl | 21 ---------- src/libarb/ArbFloat.jl | 23 +++++----- src/values/constructors.jl | 18 ++++---- test/complex.jl | 73 ++++++++++++++++++++++++++++++-- 7 files changed, 133 insertions(+), 121 deletions(-) diff --git a/.gitignore b/.gitignore index f7635ba5..305731c4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.jl.*.cov *.jl.mem *.jl.*.mem +lcov.info Manifest.toml settings.json deps/deps.jl diff --git a/README.md b/README.md index 2adeb516..24648226 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,19 @@ -# + # ArbNumerics.jl +**Copyright © 2015-2023 by Jeffrey Sarnoff.** -#### Copyright © 2015-2023 by Jeffrey Sarnoff. -#### This work is released under The MIT License. +**This work is released under The MIT License.** For multiprecision numerical computing using values with 25..2,500 digits. With arithmetic and higher level mathematics, this package offers you the best balance of performance and accuracy. This package uses the [Arb C Library](http://arblib.org/index.html), and adapts some C library interface work from [Nemo](https://github.com/wbhart/Nemo.jl) (see [_below_](https://github.com/JeffreySarnoff/ArbNumerics.jl/blob/master/README.md#acknowledgements)). Here is a presentation by the designer and architect of the [Arb C library](https://fredrikj.net/math/oxford2019.pdf). - ----- [![Travis Build Status](https://travis-ci.org/JeffreySarnoff/ArbNumerics.jl.svg?branch=master)](https://travis-ci.org/JeffreySarnoff/ArbNumerics.jl)   [![Docs](https://img.shields.io/badge/docs-stable-blue.svg)](http://jeffreysarnoff.github.io/ArbNumerics.jl/stable/)   [![Docs](https://img.shields.io/badge/docs-dev-blue.svg)](http://jeffreysarnoff.github.io/ArbNumerics.jl/dev/) - ----- +----- ## Introduction @@ -23,7 +21,6 @@ ArbNumerics exports three types: `ArbFloat`, `ArbReal`, `ArbComplex`. `ArbFloat While the bounds of an `ArbReal` or `ArbComplex` are available, the default is to show these values as digit sequences which almost assuredly are accurate, in a round to nearest sense, to the precision displayed. Math with `ArbFloat` does not provide the assurance one gets using `ArbReal`, as an `ArbFloat` is a point value. While some effort has been taken to provide you with more reliable results from math with `ArbFloat` values than would be the case using the underlying library itself, `ArbReal` or `ArbComplex` are suggested for work that is important to you. `ArbFloat` is appropriate when exactness is not required during development, or with applications that are approximating something at increasing precisions. - ## Installation ```julia @@ -38,7 +35,7 @@ When updating ArbNumerics, do `pkg> gc` to prevent accruing a great deal of unus ## StartUp `using ArbNumerics` -or, if you installed Readables, +or, if you installed Readables, `using ArbNumerics, Readables` ## Precision @@ -49,23 +46,23 @@ Otherwise, some extra bits are used to assist with printing values rounded to th You can set the internal working precision (which is the same as the displayed precision with `setextrabits(0)`) to a given number of bits or a given number of decimal digits: -`setworkingprecision(ArbFloat, bits=250)`, `setworkingprecision(ArbReal, digits=100)` +`setworkingprecision(ArbFloat, bits=250)`, `setworkingprecision(ArbReal, digits=100)` The type can be any of `ArbFloat`, `ArbReal`, `ArbComplex`. All types share the same precision so interconversion makes sense. You can set the external displayed precision (which is the the same as the internal precision with `setextrabits(0)`) to a given number of bits or a given number of decimal digits: -`setprecision(ArbFloat, bits=250)`, `setworkingprecision(ArbReal, digits=100)` +`setprecision(ArbFloat, bits=250)`, `setworkingprecision(ArbReal, digits=100)` The type can be any of `ArbFloat`, `ArbReal`, `ArbComplex`. All types share the same precision so interconversion makes sense. - -## Using ArbFloat +## Using ArbFloat Reading the sections that follow gives you a good platform from which to develop. - consider `using ArbNumerics, Readables` + ```julia julia> ArbFloat(pi, digits=30, base=10) 3.14159265358979323846264338328 @@ -82,7 +79,8 @@ Initially, the default precision is set to 106 bits. All ArbNumeric types use t The precision in use may be set globally, as with BigFloats, or it may be given with the constructor. For most purposes, you should work with a type at one, two, or three precisions. It is helps clarity to convert precisions explicitly, however, it is not necessary. -#### Constructors using the default precision +### Constructors using the default precision + ```julia julia> a = ArbFloat(3) 3.0000000000000000000000000000000 @@ -95,42 +93,39 @@ julia> c = one(ArbComplex) ``` #### Constructors using a specified precision + ```julia julia> BITS = 53; -julia> a = sqrt(ArbFloat(2, BITS)) +julia> a = sqrt(ArbFloat(2, bits=BITS)) 1.414213562373095 -julia> b = ArbReal(pi, BITS) +julia> b = ArbReal(pi, bits=BITS) 3.141592653589793 -julia> c = ArbComplex(a, b, BITS) +julia> c = ArbComplex(a, b, bits=BITS) 1.414213562373095 + 3.141592653589793im ``` + ```julia julia> DIGITS = 78; -julia> ArbFloat(pi, bits4digits(DIGITS)) +julia> ArbFloat(pi, digits=DIGITS) 3.14159265358979323846264338327950288419716939937510582097494459230781640628621 julia> DIGITS == length(string(ans)) - 1 # (-1 for the decimal point) true ``` + ### changing precision ```julia -julia> a = ArbFloat(2, 25) -2.000000 -julia> a = ArbFloat(a, 50) -2.00000000000000 - -julia> precision = 25 -julia> a = ArbFloat(2, precision) +julia> a = ArbFloat(2, bits=25) 2.000000 -julia> precision = 50 -julia> a = ArbFloat(a, precision) +julia> a = ArbFloat(a, bits=50) 2.00000000000000 ``` + ### interconversion ```julia @@ -153,11 +148,11 @@ julia> Float16(c) Float16(1.414) ``` ----- +----- Consider using ArbReals instead of ArbFloats if you want your results to be rock solid. That way you can examine the enclosures for your results with `radius(value)` or `bounds(value)`. This is strongly suggested when working with precisions that you are increasing dynamically. ----- +----- ### Math @@ -165,7 +160,7 @@ Consider using ArbReals instead of ArbFloats if you want your results to be rock - `+`,`-`, `*`, `/` - `square`, `cube`, `sqrt`, `cbrt`, `hypot` -- `pow(x,i)`, `root(x,i)` _where i is an integer > 0_ +- `pow(x, i)`, `root(x, i)` _where i is an integer > 0_ - `factorial`, `doublefactorial`, `risingfactorial` - `binomial` @@ -208,6 +203,7 @@ Consider using ArbReals instead of ArbFloats if you want your results to be rock - `elliptic_zeta`, `elliptic_sigma` ##### elliptic integrals of squared modulus + - `elliptic_e2`, `elliptic_k2` - `elliptic_p2`, `elliptic_pi2` - `elliptic_zeta2`, `elliptic_sigma2` @@ -218,10 +214,10 @@ Consider using ArbReals instead of ArbFloats if you want your results to be rock - `weierstrass_zeta`, `weierstrass_sigma` #### hypergeometric functions - + - `hypgeom0f1`, `hypgeom1f1`, `hypgeom1f2` - `hypgeom0f1reg`, `hypgeom1f1reg`, `hypgeom1f2reg` (regularized) - + #### other special functions - `ei`, `si`, `ci` @@ -241,13 +237,13 @@ Consider using ArbReals instead of ArbFloats if you want your results to be rock - `dft`, `inverse_dft` - see docs for use -## Intervals +### Intervals #### parts -- midpoint, radius -- upperbound, lowerbound, bounds -- upperbound_abs, lowerbound_abs, bounds_abs +- `midpoint`, `radius` +- `upperbound`, `lowerbound`, `bounds` +- `upperbound_abs`, `lowerbound_abs`, `bounds_abs` #### construction @@ -267,26 +263,26 @@ The radii are kept using an Arb C library internal structure that has a 30 bit u When constructing intervals , you should scale the radius to be as small as possible while preserving enclosure. ----- +----- ### a caution for BigFloat ```julia -julia> p=64;setprecision(BigFloat,p); +julia> p = 64; setprecision(BigFloat, p); -julia> ArbFloat(pi,p+8) +julia> ArbFloat(pi, bits=p+8) 3.14159265358979323846 -julia> ArbFloat(pi,p),BigFloat(pi) +julia> ArbFloat(pi, bits=p), BigFloat(pi) (3.141592653589793238, 3.14159265358979323851) -julia> [ArbFloat(pi,p), BigFloat(pi)] -2-element Array{ArbFloat{88},1}: +julia> [ArbFloat(pi, bits=p), BigFloat(pi)] +2-element Vector{ArbFloat{88}}: 3.141592653589793238 3.141592653589793239 ``` ----- +----- ## The Arb C library @@ -300,18 +296,18 @@ julia> [ArbFloat(pi,p), BigFloat(pi)] - The code is thread-safe, portable, and extensively tested. The library outperforms others. - ## Acknowledgements This work develops parts the Arb C library within Julia. It is entirely dependent on Arb by Fredrik Johansson and would not exist without the good work of William Hart, Tommy Hofmann and the Nemo.jl team. The libraries for `Arb` and `Flint`, and build file are theirs, used with permission. ----- +----- ## Alternatives For a numeric types like `Float64` and `ComplexF64` with about twice their precision, [Quadmath.jl](https://github.com/JuliaMath/Quadmath.jl) exports `Float128` and `ComplexF128`. For almost as much precision with better performance, [DoubleFloats.jl](https://github.com/JuliaMath/DoubleFloats.jl) exports `Double64` and `ComplexDF64`. ValidatedNumerics.jl and other packages available at [JuliaIntervals](https://github.com/JuliaIntervals) provide an alternative approach to developing correctly contained results. Those packages are very good and worthwhile when you do not require multiprecision numerics. ----- +----- + ## notes - To propose internal changes, please use pull requests. diff --git a/src/float/prearith.jl b/src/float/prearith.jl index 8531ba99..3ef0405a 100644 --- a/src/float/prearith.jl +++ b/src/float/prearith.jl @@ -91,36 +91,10 @@ abs2(x::ArbFloat{P}) where {P} = square( abs(x) ) abs2(x::ArbReal{P}) where {P} = square( abs(x) ) abs2(x::ArbComplex{P}) where {P} = square( abs(x) ) +# needed for GenericLinearAlgebra - -flipsign(x::ArbFloat{P}, y::U) where {P, U<:Unsigned} = +x -copysign(x::ArbFloat{P}, y::U) where {P, U<:Unsigned} = +x - -flipsign(x::ArbFloat{P}, y::S) where {P, S<:Signed} = signbit(y) ? -x : x -copysign(x::ArbFloat{P}, y::S) where {P, S<:Signed} = signbit(y) ? -abs(x) : abs(x) - -flipsign(x::ArbFloat{P}, y::F) where {P, F<:AbstractFloat} = signbit(y) ? -x : x -copysign(x::ArbFloat{P}, y::F) where {P, F<:AbstractFloat} = signbit(y) ? -abs(x) : abs(x) - -flipsign(x::ArbReal{P}, y::U) where {P, U<:Unsigned} = +x -copysign(x::ArbReal{P}, y::U) where {P, U<:Unsigned} = +x - -flipsign(x::ArbReal{P}, y::S) where {P, S<:Signed} = signbit(y) ? -x : x -copysign(x::ArbReal{P}, y::S) where {P, S<:Signed} = signbit(y) ? -abs(x) : abs(x) - -flipsign(x::ArbReal{P}, y::F) where {P, F<:AbstractFloat} = signbit(y) ? -x : x -copysign(x::ArbReal{P}, y::F) where {P, F<:AbstractFloat} = signbit(y) ? -abs(x) : abs(x) - -flipsign(x::ArbComplex{P}, y::U) where {P, U<:Unsigned} = +x -copysign(x::ArbComplex{P}, y::U) where {P, U<:Unsigned} = +x - -flipsign(x::ArbComplex{P}, y::S) where {P, S<:Signed} = signbit(y) ? -x : x -copysign(x::ArbComplex{P}, y::S) where {P, S<:Signed} = signbit(y) ? -abs(x) : abs(x) - -flipsign(x::ArbComplex{P}, y::F) where {P, F<:AbstractFloat} = signbit(y) ? -x : x -copysign(x::ArbComplex{P}, y::F) where {P, F<:AbstractFloat} = signbit(y) ? (signbit(x.re) ? x : -x) : x - - +flipsign(x::ArbNumber, y::Real) = signbit(y) ? -x : x +copysign(x::ArbNumber, y::Real) = (signbit(y) == signbit(x)) ? x : -x inv(x::ArbFloat{P}) where {P} = ArbFloat{P}( inv(ArbReal{P}(x)) ) diff --git a/src/libarb/ArbComplex.jl b/src/libarb/ArbComplex.jl index b83b4feb..b5850ca8 100644 --- a/src/libarb/ArbComplex.jl +++ b/src/libarb/ArbComplex.jl @@ -368,27 +368,6 @@ function Base.angle(x::ArbComplex{P}) where {P} !(signbit(a) || signbit(T(pi) - a)) ? a : (signbit(a) ? zero(T) : T(pi)) end -# needed for GenericLinearAlgebra - -flipsign(x::ArbComplex{P}, y::T) where {P, T<:Base.IEEEFloat} = - signbit(y) ? -x : x -flipsign(x::ArbComplex{P}, y::T) where {P, T<:Real} = - signbit(y) ? -x : x -flipsign(x::ArbComplex{P}, y::T) where {P, T<:ArbFloat} = - signbit(y) ? -x : x -flipsign(x::ArbComplex{P}, y::T) where {P, T<:ArbReal} = - signbit(y) ? -x : x - -copysign(x::ArbComplex{P}, y::T) where {P, T<:Base.IEEEFloat} = - signbit(y) ? (signbit(x) ? x : -x) : (signbit(x) ? -x : x) -copysign(x::ArbComplex{P}, y::T) where {P, T<:Real} = - signbit(y) ? (signbit(x) ? x : -x) : (signbit(x) ? -x : x) -copysign(x::ArbComplex{P}, y::T) where {P, T<:ArbFloat} = - signbit(y) ? (signbit(x) ? x : -x) : (signbit(x) ? -x : x) -copysign(x::ArbComplex{P}, y::T) where {P, T<:ArbReal} = - signbit(y) ? (signbit(x) ? x : -x) : (signbit(x) ? -x : x) - - # a type specific hash function helps the type to 'just work' const hash_arbcomplex_lo = (UInt === UInt64) ? 0x76143ad985246e79 : 0x5b6a64dc const hash_0_arbcomplex_lo = hash(zero(UInt), hash_arbcomplex_lo) diff --git a/src/libarb/ArbFloat.jl b/src/libarb/ArbFloat.jl index 325bf3cf..67580ac5 100644 --- a/src/libarb/ArbFloat.jl +++ b/src/libarb/ArbFloat.jl @@ -126,17 +126,14 @@ end Int32(x::ArbFloat{P}, roundingmode::RoundingMode) where {P} = Int32(Int64(x), roundingmode) Int16(x::ArbFloat{P}, roundingmode::RoundingMode) where {P} = Int16(Int64(x), roundingmode) -BigFloat(x::ArbFloat{P}) where {P} = BigFloat(x, RoundNearest) -function BigFloat(x::ArbFloat{P}, roundingmode::RoundingMode) where {P} - rounding = match_rounding_mode(roundingmode) - z = BigFloat(0, workingprecision(x)) - roundingdir = ccall(@libarb(arf_get_mpfr), Cint, (Ref{BigFloat}, Ref{ArbFloat}, Cint), z, x, rounding) - return z -end -BigFloat(x::ArbFloat{P}, bitprecision::Int) where {P} = BigFloat(x, bitprecision, RoundNearest) -function BigFloat(x::ArbFloat{P}, bitprecision::Int, roundingmode::RoundingMode) where {P} +""" + BigFloat(::ArbFloat; [precision=workingprecision(x), roundingmode=RoundNearest]) + +Construct a `BigFloat`from an `ArbFloat`. +""" +function BigFloat(x::ArbFloat{P}; precision::Int=workingprecision(x), roundingmode::RoundingMode=RoundNearest) where {P} rounding = match_rounding_mode(roundingmode) - z = BigFloat(0, bitprecision) + z = BigFloat(0; precision) roundingdir = ccall(@libarb(arf_get_mpfr), Cint, (Ref{BigFloat}, Ref{ArbFloat}, Cint), z, x, rounding) return z end @@ -192,7 +189,7 @@ function divrem(x::ArbFloat{P}, y::ArbFloat{P}) where {P} end fld(x::ArbFloat{P}, y::ArbFloat{P}) where {P} = - floor(x / y) + floor(x / y) mod(x::ArbFloat{P}, y::ArbFloat{P}) where {P} = x - (fld(x,y) * y) @@ -204,9 +201,9 @@ function fldmod(x::ArbFloat{P}, y::ArbFloat{P}) where {P} end cld(x::ArbFloat{P}, y::ArbFloat{P}) where {P} = - ceil(x / y) + ceil(x / y) + - # a type specific hash function helps the type to 'just work' const hash_arbfloat_lo = (UInt === UInt64) ? 0x37e642589da3416a : 0x5d46a6b4 const hash_0_arbfloat_lo = hash(zero(UInt), hash_arbfloat_lo) diff --git a/src/values/constructors.jl b/src/values/constructors.jl index 0a30d1fa..9fb62620 100644 --- a/src/values/constructors.jl +++ b/src/values/constructors.jl @@ -103,7 +103,7 @@ Complex(x::ArbComplex{P}) where {P} = Complex{Float64}(x, RoundNearest) for I in (:Int8, :Int16, :Int32, :Int64, :Int128) @eval begin - + function $I(x::ArbFloat{P}) where {P} !isinteger(x) && throw(InexactError("$(x) is not an integer")) bi = BigInt(BigFloat(x)) @@ -111,20 +111,20 @@ for I in (:Int8, :Int16, :Int32, :Int64, :Int128) return $I(bi) end - function $I(x::ArbReal{P}) where {P} + function $I(x::ArbReal{P}) where {P} (!isexact(x) | !isinteger(x)) && throw(InexactError("$(x) is not an integer")) bi = BigInt(BigFloat(x)) !(typemin($I) <= bi <= typemax($I)) && throw(InexactError("$(x)")) return $I(bi) end - function $I(x::ArbComplex{P}) where {P} + function $I(x::ArbComplex{P}) where {P} (!isexact(x) | !isinteger(x) | !iszero(imag(x))) && throw(InexactError("$(x) is not an integer")) bi = BigInt(BigFloat(x)) !(typemin($I) <= bi <= typemax($I)) && throw(InexactError("$(x)")) return $I(bi) - end - + end + end end @@ -140,20 +140,20 @@ ArbComplex(x::T, y::T) where {S, T<:Rational{S}} = ArbComplex(ArbReal(x), ArbRea function ArbReal{P}(x::Irrational{S}) where {P,S} mid = ArbFloat{P}(x) rad = ulp(mid) - return setball(mid, rad) + return setball(mid, rad) end function ArbReal(x::Irrational{S}) where {S} P = workingprecision(ArbReal) mid = ArbFloat{P}(x) rad = ulp(mid) - return setball(mid, rad) + return setball(mid, rad) end ArbComplex(x::Irrational{S}) where {S} = ArbComplex(ArbReal(x), ArbReal(0)) ArbComplex{P}(x::Irrational{S}) where {P,S} = ArbComplex{P}(ArbReal{P}(x), ArbReal{P}(0)) -ArbComplex(x::Irrational{S}, y::Real) where {S} = ArbComplex(ArbReal(x), ArbReal(0)) -ArbComplex{P}(x::Irrational{S}, y::Real) where {P,S} = ArbComplex{P}(ArbReal{P}(x), ArbReal{P}(0)) +ArbComplex(x::Irrational{S}, y::Real) where {S} = ArbComplex(ArbReal(x), ArbReal(y)) +ArbComplex{P}(x::Irrational{S}, y::Real) where {P,S} = ArbComplex{P}(ArbReal{P}(x), ArbReal{P}(y)) # fallback diff --git a/test/complex.jl b/test/complex.jl index 2edd99c1..7d6aaa8e 100644 --- a/test/complex.jl +++ b/test/complex.jl @@ -1,20 +1,85 @@ + +import ArbNumerics: sign_bit, sign_bits + @testset "ArbComplex" begin @testset "complex" begin @test (1 + im) / ArbNumerics.ArbComplex(1, 1) == 1 + @test one(ArbComplex) == 1 + BITS = 53 + a = sqrt(ArbFloat(2, bits=BITS)) + b = ArbReal(pi, bits=BITS) + c = ArbComplex(a, b, bits=BITS) + @test precision(c) == BITS end # issue 34 and similar Cint-related stuff @testset "ArbComplex constructors" begin - @test_throws ErrorException ArbComplex(5.0, base = 3) - @test_throws ErrorException ArbComplex(5.0,1.0, base = 3) - @test ArbComplex(5, digits = 53) == ArbComplex{53}(5) - @test ArbComplex(5, 0, digits = 53) == ArbComplex{53}(5, 0) + @test ismissing(ArbComplex{100}(missing)) + @test_throws ErrorException ArbComplex(5.0, base=3) + @test_throws ErrorException ArbComplex(5.0, 1.0, base=3) + @test ArbComplex{128}(1) == 1 + ac = ArbComplex{64}(1.0 + 2im) + rac = real(ac) + fac = float(rac) + @test abs(ac) ≈ sqrt(abs2(fac) + abs2(imag(ac))) + @test ArbComplex{64}(ac) === ac + @test ArbComplex{64}(fac) == ArbComplex(fac) + @test ArbComplex(5, digits=53) == 5 + @test ArbComplex(5, 0, digits=53) == 5 @test ArbComplex{53}(1 + 2im) == 1 + 2im + @test sign_bit(ac) == false + @test sign_bits(ac) == (false, false) + @test ArbComplex(fac) == ArbComplex(rac) + @test copy(ac) == deepcopy(ac) + @test ArbComplex(rac, fac) == Complex(rac, fac) == complex(rac, fac) + @test ArbComplex(rac) == Complex(rac) == complex(rac) + + @test ArbComplex(rac, Int128(1)) isa ArbComplex{64} + @test ArbComplex(fac, UInt8(1)) isa ArbComplex{128} # inconsistent for fac::ArbFloat + @test ArbComplex(Float32(1.0), fac) isa ArbComplex{64} + @test ArbComplex{25}(1, fac) isa ArbComplex{25} + @test ArbComplex(π) isa ArbComplex{128} + @test ArbComplex(Int32(-1)) == ArbComplex(-1.0) @test ArbComplex(Int32(-1), Int32(-2)) == ArbComplex(-1.0, -2.0) @test ArbComplex(typemax(Int32) + 1) == ArbComplex(float(typemax(Int32) + 1)) @test hash(ArbComplex(-2, -2)) isa UInt + @test_broken ArbComplex{256}(1.0 + im) == ArbComplex(1 + im, bits=256) # TODO missing + @test ArbComplex{256}(1.0 + im) == ArbComplex(1, 1, bits=256) + + @test midpoint(ac) == ac + @test 1e-38 <= abs(radius(ArbComplex(π))) <= 2e-38 + api = ArbComplex(pi, -pi) + @test trunc(api) == ArbComplex(3 - 3im) + @test floor(api) == ArbComplex(3 - 4im) + @test ceil(api) == ArbComplex(4 - 3im) + @test trunc(Int, api) == (3, -3) + @test floor(Int8, api) == (3, -4) + @test ceil(Int16, api) == (4, -3) + @test modf(ArbComplex(3.5, -2.5)) == ((0.5, 3.0), (-0.5, -2.0)) + @test fmod(modf(api)...) == api + @test_broken angle(api) ≈ angle(Complex(api)) + + @test flipsign(api, -1) == -api + @test flipsign(api, 0.0) == api + @test flipsign(api, ArbFloat(-1)) == -api + @test flipsign(api, ArbReal(0.0)) == api + + @test copysign(-api, -1) == -api + @test copysign(-api, 0.0) == api + @test copysign(-api, ArbFloat(-1)) == -api + @test copysign(-api, ArbReal(0.0)) == api + + @test sign(api) == 1 + @test signs(api) == (1, -1) + @test signs(ArbComplex(0)) == (0, 0) + + @test signbit(api) == false + @test signbits(api) == (false, true) + + @test abs2(api) ≈ abs2(real(api)) + abs2(imag(api)) + end end