Current section

59 Versions

Jump to

Compare versions

6 files changed
+113 additions
-28 deletions
  @@ -1,3 +1,11 @@
1 + ## Changelog for Cldr Utils version 2.0.5
2 +
3 + This is the changelog for Cldr Utils v2.0.4 released on Jnauary 3rd, 2018. For older changelogs please consult the release tag on [GitHub](https://github.com/kipcole9/cldr_utils/tags)
4 +
5 + ### Bug Fixes
6 +
7 + * Fixes `Cldr.Math.round/3` for floats when rounding is > 0 digits
8 +
1 9 ## Changelog for Cldr Utils version 2.0.4
2 10
3 11 This is the changelog for Cldr Utils v2.0.4 released on Decmber 15th, 2018. For older changelogs please consult the release tag on [GitHub](https://github.com/kipcole9/cldr_utils/tags)
  @@ -4,7 +4,7 @@
4 4 <<"Map, Calendar, Digits, Macro, Math and String helpers for ex_cldr">>}.
5 5 {<<"elixir">>,<<"~> 1.5">>}.
6 6 {<<"files">>,
7 - [<<"lib">>,<<"lib/cldr">>,<<"lib/cldr/.DS_Store">>,<<"lib/cldr/utils">>,
7 + [<<"lib">>,<<"lib/cldr">>,<<"lib/cldr/utils">>,
8 8 <<"lib/cldr/utils/calendar_conversion.ex">>,<<"lib/cldr/utils/digits.ex">>,
9 9 <<"lib/cldr/utils/helpers.ex">>,<<"lib/cldr/utils/macros.ex">>,
10 10 <<"lib/cldr/utils/map.ex">>,<<"lib/cldr/utils/math.ex">>,
  @@ -14,10 +14,10 @@
14 14 {<<"licenses">>,[<<"Apache 2.0">>]}.
15 15 {<<"links">>,
16 16 [{<<"Changelog">>,
17 - <<"https://github.com/kipcole9/cldr_utils/blob/v2.0.4/CHANGELOG.md">>},
17 + <<"https://github.com/kipcole9/cldr_utils/blob/v2.0.5/CHANGELOG.md">>},
18 18 {<<"GitHub">>,<<"https://github.com/kipcole9/cldr_utils">>},
19 19 {<<"Readme">>,
20 - <<"https://github.com/kipcole9/cldr_utils/blob/v2.0.4/README.md">>}]}.
20 + <<"https://github.com/kipcole9/cldr_utils/blob/v2.0.5/README.md">>}]}.
21 21 {<<"name">>,<<"cldr_utils">>}.
22 22 {<<"requirements">>,
23 23 [[{<<"app">>,<<"decimal">>},
  @@ -25,4 +25,4 @@
25 25 {<<"optional">>,false},
26 26 {<<"repository">>,<<"hexpm">>},
27 27 {<<"requirement">>,<<"~> 1.5">>}]]}.
28 - {<<"version">>,<<"2.0.4">>}.
28 + {<<"version">>,<<"2.0.5">>}.
  @@ -177,6 +177,7 @@ defmodule Cldr.Math do
177 177 This is very likely to lose precision - lots of numbers won't
178 178 make the round trip conversion. Use with care. Actually, better
179 179 not to use it at all.
180 +
180 181 """
181 182 @spec to_float(%Decimal{}) :: float
182 183 def to_float(%Decimal{sign: sign, coef: coef, exp: exp}) do
  @@ -775,6 +776,13 @@ defmodule Cldr.Math do
775 776 Decimal.round(number, places, mode)
776 777 end
777 778
779 + def round(number, places, mode) when is_integer(number) do
780 + number
781 + |> Decimal.new
782 + |> Decimal.round(places, mode)
783 + |> Decimal.to_integer
784 + end
785 +
778 786 # Consistent with Kernel.round/1
779 787 def round(number, 0 = places, :half_up = mode) when is_float(number) do
780 788 number
  @@ -786,15 +794,98 @@ defmodule Cldr.Math do
786 794
787 795 def round(number, places, mode) when is_float(number) do
788 796 number
789 - |> Decimal.from_float
790 - |> Decimal.round(places, mode)
791 - |> to_float
797 + |> Digits.to_digits()
798 + |> round_digits(%{decimals: places, rounding: mode})
799 + |> Digits.to_number(number)
792 800 end
793 801
794 - def round(number, places, mode) when is_integer(number) do
795 - number
796 - |> Decimal.new
797 - |> Decimal.round(places, mode)
798 - |> Decimal.to_integer
802 + # The next function heads operate on decomposed numbers returned
803 + # by Digits.to_digits.
804 +
805 + # scientific/decimal rounding are the same, we are just varying which
806 + # digit we start counting from to find our rounding point
807 + def round_digits(digits_t, options)
808 +
809 + # Passing true for decimal places avoids rounding and uses whatever is necessary
810 + def round_digits(digits_t, %{scientific: true}), do: digits_t
811 + def round_digits(digits_t, %{decimals: true}), do: digits_t
812 +
813 + # rounded away all the decimals... return 0
814 + def round_digits(_, %{scientific: dp}) when dp <= 0, do: {[0], 1, true}
815 + def round_digits({_, place, _}, %{decimals: dp}) when dp + place <= 0, do: {[0], 1, true}
816 +
817 + def round_digits(digits_t = {_, place, _}, options = %{decimals: dp}) do
818 + {digits, place, sign} = do_round(digits_t, dp + place - 1, options)
819 + {List.flatten(digits), place, sign}
820 + end
821 +
822 + def round_digits(digits_t, options = %{scientific: dp}) do
823 + {digits, place, sign} = do_round(digits_t, dp, options)
824 + {List.flatten(digits), place, sign}
825 + end
826 +
827 + defp do_round({digits, place, positive}, round_at, %{rounding: rounding}) do
828 + case Enum.split(digits, round_at) do
829 + {l, [least_sig | [tie | rest]]} ->
830 + case do_incr(l, least_sig, increment?(positive, least_sig, tie, rest, rounding)) do
831 + [:rollover | digits] -> {digits, place + 1, positive}
832 + digits -> {digits, place, positive}
833 + end
834 +
835 + {l, [least_sig | []]} ->
836 + {[l, least_sig], place, positive}
837 +
838 + {l, []} ->
839 + {l, place, positive}
799 840 end
800 841 end
842 +
843 + # Helper functions for round/2-3
844 + defp do_incr(l, least_sig, false), do: [l, least_sig]
845 + defp do_incr(l, least_sig, true) when least_sig < 9, do: [l, least_sig + 1]
846 + # else need to cascade the increment
847 + defp do_incr(l, 9, true) do
848 + l
849 + |> Enum.reverse()
850 + |> cascade_incr
851 + |> Enum.reverse([0])
852 + end
853 +
854 + # cascade an increment of decimal digits which could be rolling over 9 -> 0
855 + defp cascade_incr([9 | rest]), do: [0 | cascade_incr(rest)]
856 + defp cascade_incr([d | rest]), do: [d + 1 | rest]
857 + defp cascade_incr([]), do: [1, :rollover]
858 +
859 + @spec increment?(boolean, non_neg_integer | nil, non_neg_integer | nil, list(), atom()) ::
860 + boolean
861 + defp increment?(positive, least_sig, tie, rest, round)
862 +
863 + # Directed rounding towards 0 (truncate)
864 + defp increment?(_, _ls, _tie, _, :down), do: false
865 + # Directed rounding away from 0 (non IEEE option)
866 + defp increment?(_, _ls, nil, _, :up), do: false
867 + defp increment?(_, _ls, _tie, _, :up), do: true
868 +
869 + # Directed rounding towards +∞ (rounding up / ceiling)
870 + defp increment?(true, _ls, tie, _, :ceiling) when tie != nil, do: true
871 + defp increment?(_, _ls, _tie, _, :ceiling), do: false
872 +
873 + # Directed rounding towards -∞ (rounding down / floor)
874 + defp increment?(false, _ls, tie, _, :floor) when tie != nil, do: true
875 + defp increment?(_, _ls, _tie, _, :floor), do: false
876 +
877 + # Round to nearest - tiebreaks by rounding to even
878 + # Default IEEE rounding, recommended default for decimal
879 + defp increment?(_, ls, 5, [], :half_even) when Integer.is_even(ls), do: false
880 + defp increment?(_, _ls, tie, _rest, :half_even) when tie >= 5, do: true
881 + defp increment?(_, _ls, _tie, _rest, :half_even), do: false
882 +
883 + # Round to nearest - tiebreaks by rounding away from zero (same as Elixir Kernel.round)
884 + defp increment?(_, _ls, tie, _rest, :half_up) when tie >= 5, do: true
885 + defp increment?(_, _ls, _tie, _rest, :half_up), do: false
886 +
887 + # Round to nearest - tiebreaks by rounding towards zero (non IEEE option)
888 + defp increment?(_, _ls, 5, [], :half_down), do: false
889 + defp increment?(_, _ls, tie, _rest, :half_down) when tie >= 5, do: true
890 + defp increment?(_, _ls, _tie, _rest, :half_down), do: false
891 + end
  @@ -1,18 +1,4 @@
1 1 defmodule CldrUtils do
2 - @moduledoc """
3 - Documentation for CldrUtils.
4 - """
2 + @moduledoc false
5 3
6 - @doc """
7 - Hello world.
8 -
9 - ## Examples
10 -
11 - iex> CldrUtils.hello()
12 - :world
13 -
14 - """
15 - def hello do
16 - :world
17 - end
18 4 end
Loading more files…