Skip to content

Commit 0b35996

Browse files
author
José Valim
committed
Optimize and improve docs for Float operations
1 parent fc5cf50 commit 0b35996

File tree

2 files changed

+49
-32
lines changed

2 files changed

+49
-32
lines changed

lib/elixir/lib/float.ex

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import Kernel, except: [round: 1]
2+
13
defmodule Float do
24
@moduledoc """
35
Functions for working with floating point numbers.
@@ -98,9 +100,13 @@ defmodule Float do
98100

99101
@doc """
100102
Rounds a float to the largest integer less than or equal to `num`.
103+
101104
Floor also accepts a precision to round a floating point value down
102105
to an arbitrary number of fractional digits (between 0 and 15).
103106
107+
This function always returns floats. One may use `Kernel.trunc/1` to
108+
truncate the result to an integer afterwards.
109+
104110
## Examples
105111
106112
iex> Float.floor(34.25)
@@ -113,25 +119,24 @@ defmodule Float do
113119
34.25
114120
115121
"""
116-
@spec floor(float) :: float
117-
def floor(num) when is_float(num) do
118-
truncated = :erlang.trunc(num)
119-
case :erlang.abs(num - truncated) do
120-
x when x > 0 and num < 0 -> truncated - 1.0
121-
_ -> truncated + 0.0
122-
end
123-
end
124-
125-
@spec floor(float, integer) :: float
126-
def floor(num, precision) when is_float(num) do
127-
calculate_precision(num, -0.5, precision) |> round(precision)
122+
@spec floor(float, 0..15) :: float
123+
def floor(number, precision \\ 0) when is_float(number) and precision in 0..15 do
124+
power = power_of_10(precision)
125+
number = number * power
126+
truncated = trunc(number)
127+
variance = if number - truncated < 0, do: -1.0, else: 0.0
128+
(truncated + variance) / power
128129
end
129130

130131
@doc """
131132
Rounds a float to the largest integer greater than or equal to `num`.
133+
132134
Ceil also accepts a precision to round a floating point value down to
133135
an arbitrary number of fractional digits (between 0 and 15).
134136
137+
This function always returns floats. One may use `Kernel.trunc/1` to
138+
truncate the result to an integer afterwards.
139+
135140
## Examples
136141
137142
iex> Float.ceil(34.25)
@@ -144,25 +149,23 @@ defmodule Float do
144149
34.26
145150
146151
"""
147-
148-
@spec ceil(float) :: float
149-
def ceil(num) when is_float(num) do
150-
truncated = :erlang.trunc(num)
151-
case :erlang.abs(num - truncated) do
152-
x when x > 0 and num > 0 -> truncated + 1.0
153-
_ -> truncated + 0.0
154-
end
155-
end
156-
157-
@spec ceil(float, integer) :: float
158-
def ceil(num, precision) when is_float(num) do
159-
calculate_precision(num, 0.5, precision) |> round(precision)
152+
@spec ceil(float, 0..15) :: float
153+
def ceil(number, precision \\ 0) when is_float(number) and precision in 0..15 do
154+
power = power_of_10(precision)
155+
number = number * power
156+
truncated = trunc(number)
157+
variance = if number - truncated > 0, do: 1.0, else: 0.0
158+
(truncated + variance) / power
160159
end
161160

162161
@doc """
163162
Rounds a floating point value to an arbitrary number of fractional digits
164163
(between 0 and 15).
165164
165+
This function only accepts floats and returns floats. Use `Kernel.round/1`
166+
if you want a function that accepts both floats and integers and always
167+
returns an integer.
168+
166169
## Examples
167170
168171
iex> Float.round(5.5674, 3)
@@ -178,9 +181,15 @@ defmodule Float do
178181
-5.568
179182
180183
"""
181-
@spec round(float, integer) :: float
182-
def round(number, precision) when is_float(number) and is_integer(precision) and precision in 0..15 do
183-
Kernel.round(number * :math.pow(10, precision)) / :math.pow(10, precision)
184+
@spec round(float, 0..15) :: float
185+
def round(number, precision \\ 0) when is_float(number) and precision in 0..15 do
186+
power = power_of_10(precision)
187+
Kernel.round(number * power) / power
188+
end
189+
190+
Enum.reduce 0..15, 1, fn x, acc ->
191+
defp power_of_10(unquote(x)), do: unquote(acc)
192+
acc * 10
184193
end
185194

186195
@doc """
@@ -260,10 +269,6 @@ defmodule Float do
260269
:erlang.float_to_binary(float, expand_compact(options))
261270
end
262271

263-
defp calculate_precision(num, variance, precision) do
264-
num + (variance / :math.pow(10, precision))
265-
end
266-
267272
defp expand_compact([{:compact, false}|t]), do: expand_compact(t)
268273
defp expand_compact([{:compact, true}|t]), do: [:compact|expand_compact(t)]
269274
defp expand_compact([h|t]), do: [h|expand_compact(t)]

lib/elixir/test/elixir/float_test.exs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ defmodule FloatTest do
4242
end
4343

4444
test :floor_with_precision do
45+
assert Float.floor(12.524235, 0) === 12.0
46+
assert Float.floor(-12.524235, 0) === -13.0
47+
48+
assert Float.floor(12.52, 2) === 12.52
49+
assert Float.floor(-12.52, 2) === -12.52
50+
4551
assert Float.floor(12.524235, 2) === 12.52
4652
assert Float.floor(-12.524235, 3) === -12.525
4753
end
@@ -61,6 +67,12 @@ defmodule FloatTest do
6167
end
6268

6369
test :ceil_with_precision do
70+
assert Float.ceil(12.524235, 0) === 13.0
71+
assert Float.ceil(-12.524235, 0) === -12.0
72+
73+
assert Float.ceil(12.52, 2) === 12.52
74+
assert Float.ceil(-12.52, 2) === -12.52
75+
6476
assert Float.ceil(12.524235, 2) === 12.53
6577
assert Float.ceil(-12.524235, 3) === -12.524
6678
end

0 commit comments

Comments
 (0)