%%% The MIT License (MIT) %%% Copyright (c) 2018-2021 Hajime Nakagami -module(efirebirdsql_decfloat). -export([decimal_fixed_to_decimal/2, decimal64_to_decimal/1, decimal128_to_decimal/1]). -spec dpd_to_int(integer()) -> integer(). dpd_to_int(Dpd) -> B0 = if Dpd band 2#0000000001 =/= 0 -> 1; true -> 0 end, B1 = if Dpd band 2#0000000010 =/= 0 -> 1; true -> 0 end, B2 = if Dpd band 2#0000000100 =/= 0 -> 1; true -> 0 end, B3 = if Dpd band 2#0000001000 =/= 0 -> 1; true -> 0 end, B4 = if Dpd band 2#0000010000 =/= 0 -> 1; true -> 0 end, B5 = if Dpd band 2#0000100000 =/= 0 -> 1; true -> 0 end, B6 = if Dpd band 2#0001000000 =/= 0 -> 1; true -> 0 end, B7 = if Dpd band 2#0010000000 =/= 0 -> 1; true -> 0 end, B8 = if Dpd band 2#0100000000 =/= 0 -> 1; true -> 0 end, B9 = if Dpd band 2#1000000000 =/= 0 -> 1; true -> 0 end, D = if B3 =:= 0 -> [ B2 * 4 + B1 * 2 + B0, B6 * 4 + B5 * 2 + B4, B9 * 4 + B8 * 2 + B7 ]; {B3, B2, B1} =:= {1, 0, 0} -> [ 8 + B0, B6 * 4 + B5 * 2 + B4, B9 * 4 + B8 * 2 + B7 ]; {B3, B2, B1} =:= {1, 0, 1} -> [ B6 * 4 + B5 * 2 + B0, 8 + B4, B9 * 4 + B8 * 2 + B7 ]; {B3, B2, B1} =:= {1, 1, 0} -> [ B9 * 4 + B8 * 2 + B0, B6 * 4 + B5 * 2 + B4, 8 + B7 ]; {B6, B5, B3, B2, B1} =:= {0, 0, 1, 1, 1} -> [ B9 * 4 + B8 * 2 + B0, 8 + B4, 8 + B7 ]; {B6, B5, B3, B2, B1} =:= {0, 1, 1, 1, 1} -> [ 8 + B0, B9 * 4 + B8 * 2 + B4, 8 + B7 ]; {B6, B5, B3, B2, B1} =:= {1, 0, 1, 1, 1} -> [ 8 + B0, 8 + B4, B9 * 4 + B8 * 2 + B7 ]; {B6, B5, B3, B2, B1} =:= {1, 1, 1, 1, 1} -> [ 8 + B0, 8 + B4, 8 + B7 ] end, lists:nth(3, D) * 100 + lists:nth(2, D) * 10 + lists:nth(1, D). -spec calc_significand_segments(integer(), list()) -> integer(). calc_significand_segments(Prefix, []) -> Prefix; calc_significand_segments(Prefix, Segments) -> [Dpd | Tail] = Segments, calc_significand_segments(Prefix * 1000 + dpd_to_int(Dpd), Tail). -spec segments_bits(integer(), integer(), list()) -> list(). segments_bits(_, 0, Segments) -> Segments; segments_bits(DpdBits, NumSegments, Segments) -> NextSegment = DpdBits band 2#1111111111, segments_bits(DpdBits bsr 10, NumSegments - 1, [NextSegment | Segments]). -spec calc_significand(non_neg_integer(), non_neg_integer(), integer()) -> non_neg_integer(). calc_significand(Prefix, DpdBits, NumBits) -> %% https://en.wikipedia.org/wiki/Decimal128_floating-point_format#Densely_packed_decimal_significand_field NumSegments = NumBits div 10, Segments = segments_bits(DpdBits, NumSegments, []), calc_significand_segments(Prefix, Segments). -spec decimal128_to_sign_digits_exponent(binary()) -> {integer(), integer(), integer()} | list(). decimal128_to_sign_digits_exponent(Bin) -> %% https://en.wikipedia.org/wiki/Decimal128_floating-point_format <> = Bin, if Combination band 2#11111000000000000 =:= 2#11111000000000000, Sign =:= 0 -> "NaN"; Combination band 2#11111000000000000 =:= 2#11111000000000000, Sign =:= 1 -> "-NaN"; Combination band 2#11111000000000000 =:= 2#11110000000000000, Sign =:= 0 -> "-Infinity"; Combination band 2#11111000000000000 =:= 2#11110000000000000, Sign =:= 1 -> "Infinity"; true -> Exponent = if Combination band 2#11000000000000000 =:= 2#00000000000000000 -> 2#00000000000000 + Combination band 2#111111111111; Combination band 2#11000000000000000 =:= 2#01000000000000000 -> 2#01000000000000 + Combination band 2#111111111111; Combination band 2#11000000000000000 =:= 2#10000000000000000 -> 2#10000000000000 + Combination band 2#111111111111; Combination band 2#11110000000000000 =:= 2#11000000000000000 -> 2#00000000000000 + Combination band 2#111111111111; Combination band 2#11110000000000000 =:= 2#11010000000000000 -> 2#01000000000000 + Combination band 2#111111111111; Combination band 2#11110000000000000 =:= 2#11100000000000000 -> 2#10000000000000 + Combination band 2#111111111111 end - 6176, Prefix = if Combination band 2#11000000000000000 =:= 2#00000000000000000 -> (Combination bsr 12) band 2#111; Combination band 2#11000000000000000 =:= 2#01000000000000000 -> (Combination bsr 12) band 2#111; Combination band 2#11000000000000000 =:= 2#10000000000000000 -> (Combination bsr 12) band 2#111; Combination band 2#11110000000000000 =:= 2#11000000000000000 -> 8 + (Combination bsr 12) band 2#1; Combination band 2#11110000000000000 =:= 2#11010000000000000 -> 8 + (Combination bsr 12) band 2#1; Combination band 2#11110000000000000 =:= 2#11100000000000000 -> 8 + (Combination bsr 12) band 2#1 end, Digits = calc_significand(Prefix, DpdBits, 110), {Sign, Digits, Exponent} end. %% ------------------------------------------------------------------------------------ -spec decimal_fixed_to_decimal(binary(), integer()) -> list(). decimal_fixed_to_decimal(Bin, Scale) -> case decimal128_to_sign_digits_exponent(Bin) of {Sign, V, _} -> efirebirdsql_conv:parse_number(Sign, V, Scale); V -> V end. -spec decimal64_to_decimal(binary()) -> list(). decimal64_to_decimal(Bin) -> %% https://en.wikipedia.org/wiki/Decimal64_floating-point_format <> = Bin, if Combination =:= 2#11111, Sign =:= 0 -> "NaN"; Combination =:= 2#11111, Sign =:= 1 -> "-NaN"; Combination =:= 2#11110, Sign =:= 0 -> "Infinity"; Combination =:= 2#11110, Sign =:= 1 -> "-Infinity"; true -> Exponent = if Combination band 2#11000 =:= 2#00000 -> 2#0000000000 + BaseExponent; Combination band 2#11000 =:= 2#01000 -> 2#0100000000 + BaseExponent; Combination band 2#11000 =:= 2#10000 -> 2#1000000000 + BaseExponent; Combination band 2#11000 =:= 2#11000 -> 2#0000000000 + BaseExponent; Combination band 2#11000 =:= 2#11010 -> 2#0100000000 + BaseExponent; Combination band 2#11000 =:= 2#11100 -> 2#1000000000 + BaseExponent end - 398, Prefix = if Combination band 2#11000 =:= 2#00000 -> Combination band 2#111; Combination band 2#11000 =:= 2#01000 -> Combination band 2#111; Combination band 2#11000 =:= 2#10000 -> Combination band 2#111; Combination band 2#11000 =:= 2#11000 -> 8 + (Combination band 2#1); Combination band 2#11000 =:= 2#11100 -> 8 + (Combination band 2#1); Combination band 2#11000 =:= 2#11100 -> 8 + (Combination band 2#1) end, Digits = calc_significand(Prefix, DpdBits, 50), efirebirdsql_conv:parse_number(Sign, Digits, Exponent) end. -spec decimal128_to_decimal(binary()) -> list(). decimal128_to_decimal(Bin) -> case decimal128_to_sign_digits_exponent(Bin) of {Sign, V, Scale} -> efirebirdsql_conv:parse_number(Sign, V, Scale); V -> V end.