Skip to content

Commit d7c2d5c

Browse files
committed
Make results of floor, trunc, ceil consistent (#76)
1 parent 5008aba commit d7c2d5c

File tree

2 files changed

+47
-4
lines changed

2 files changed

+47
-4
lines changed

src/FixedPointDecimals.jl

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,17 @@ function ceil{T, f}(x::FD{T, f})
125125
end
126126
end
127127

128-
for truncfn in [:trunc, :floor, :ceil]
129-
@eval $truncfn{TI <: Integer}(::Type{TI}, x::FD)::TI = $truncfn(x)
128+
for fn in [:trunc, :floor, :ceil]
129+
@eval $fn{TI <: Integer}(::Type{TI}, x::FD)::TI = $fn(x)
130130

131131
# round/trunc/ceil/flooring to FD; generic
132132
# TODO. this is probably incorrect for floating point and we need to check
133133
# overflow in other cases.
134-
@eval function $truncfn{T, f}(::Type{FD{T, f}}, x::Real)
135-
reinterpret(FD{T, f}, $truncfn(T, T(10)^f * x))
134+
@eval function $fn{T, f}(::Type{FD{T, f}}, x::Real)
135+
powt = T(10)^f
136+
val = trunc(T, x)
137+
val = val * powt + $fn(T, (x - val) * powt)
138+
reinterpret(FD{T, f}, val)
136139
end
137140
end
138141
round{TI <: Integer}(::Type{TI}, x::FD,

test/runtests.jl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
using FixedPointDecimals
2+
import FixedPointDecimals: FD
23
using Base.Test
34
using Compat
45

56
const SFD2 = FixedDecimal{Int16, 2}
67
const SFD4 = FixedDecimal{Int16, 4}
78
const FD1 = FixedDecimal{Int, 1}
89
const FD2 = FixedDecimal{Int, 2}
10+
const FD3 = FixedDecimal{Int, 3}
911
const FD4 = FixedDecimal{Int, 4}
1012
const WFD2 = FixedDecimal{Int128, 2}
1113
const WFD4 = FixedDecimal{Int128, 4}
@@ -37,12 +39,22 @@ const keyvalues = Dict(
3739
reinterpret(WFD4, 164435910993133062409572187012743929911),
3840
typemax(WFD4)])
3941

42+
# Floating point values written as integer strings. Useful for testing behaviours of
43+
# trunc, floor, and ceil.
44+
const INT_2_2 = "22000000000000001776356839400250464677" # 2.2
45+
const INT_2_3 = "22999999999999998223643160599749535322" # 2.3
46+
47+
4048
# numbers that may cause overflow
4149
islarge(x) = x == typemin(x) || abs(x) > 1000
4250

4351
# numbers that can never cause overflow
4452
issmall(x) = -1 < x 1
4553

54+
function parse_int{T, f}(::Type{FD{T, f}}, val::AbstractString; ceil::Bool=false)
55+
reinterpret(FD{T, f}, parse(T, val[1:(f + 1)]) + T(ceil))
56+
end
57+
4658
@testset "conversion" begin
4759
@testset for x in keyvalues[FD2]
4860
@testset for T in [Rational{Int128}, WFD2, WFD4]
@@ -285,6 +297,16 @@ end
285297
@test isinteger(Base.widemul(10, FD2(trunc(FD1, x))))
286298
@test abs(FD2(trunc(FD1, x))) 0
287299
end
300+
301+
@testset "truncate precision" begin
302+
@test trunc(FD2, 2.3) != trunc(FD3, 2.3)
303+
@test trunc(FD2, 2.3) == FD2(2.29)
304+
@test trunc(FD3, 2.3) == FD3(2.299)
305+
306+
for f in 0:12
307+
trunc(FD{Int64, f}, 2.3) == parse_int(FD{Int64, f}, INT_2_3)
308+
end
309+
end
288310
end
289311

290312
# eps that works for integers too
@@ -303,6 +325,24 @@ epsi{T}(::Type{T}) = eps(T)
303325
@test ceil(T, x) - epsi(T) < x ceil(T, x)
304326
end
305327
end
328+
329+
@testset "floor, ceil precision" begin
330+
@test floor(FD2, 2.3) != floor(FD3, 2.3)
331+
@test floor(FD2, 2.3) == FD2(2.29)
332+
@test floor(FD3, 2.3) == FD3(2.299)
333+
334+
for f in 0:12
335+
floor(FD{Int64, f}, 2.3) == parse_int(FD{Int64, f}, INT_2_3)
336+
end
337+
338+
@test ceil(FD2, 2.2) != ceil(FD3, 2.2)
339+
@test ceil(FD2, 2.2) == FD2(2.21)
340+
@test ceil(FD3, 2.2) == FD3(2.201)
341+
342+
for f in 0:12
343+
ceil(FD{Int64, f}, 2.2) == parse_int(FD{Int64, f}, INT_2_2, ceil=true)
344+
end
345+
end
306346
end
307347

308348
@testset "show" begin

0 commit comments

Comments
 (0)