how to convert a large number exprimed on several bytes? - erlang

If a number is exprimed on 4 bytes, from LSB to MSB, how to convert it in integer ?
example:
<<77,0,0,0>> shall give 77
but
<<0,1,0,0>> shall give 256
Let S = <<0,1,0,0>>,
<<L1,L2,L3,L4>> = S,
L = L1*1 + L2*256 + L3*65536 + L4*16777216,
But it's not elegant ...

The bit syntax in Erlang does this in a very straightforward way:
<<A:32/little>> = <<0,1,0,0>>,
A.
% A = 256
or as a function:
decode(<<Int:32/little>>) -> Int.
% decode(<<0,1,0,0>>) =:= 256.

EDIT (this is the correct answer, and sorry for discovering it late...)
> binary:decode_unsigned(<<0,1,0,0>>,little).
256
The easier way would be something like:
decode_my_binary( <<A,B,C,D>> ) ->
A + B*256 + C*65536 + D*16777216.
EDIT:
As per your edit, if you find this one not very elegant, you can try other approaches. Still I think the above is the correct way of doing it. You can write a recursive function (not tested, but you get the idea):
decode( B ) -> decode(binary_to_list(B), 0, 1).
decode( [], R, _ ) -> R;
decode( [H|T], R, F) ->
decode(T, R + H*F, F*256).
but this is clearly slower. Another possibility is to have the list of the binary digits and the list of multipliers and then fold it:
lists:sum(lists:zipwith( fun(X,Y) -> X*Y end,
binary_to_list(B), [ math:pow(256,X) || X <- [0,1,2,3] ])).
Or if you want a variable number of digits:
fun(Digits) ->
lists:sum(lists:zipwith( fun(X,Y) -> X*Y end,
binary_to_list(B), [ math:pow(256,X) || X <- lists:seq(0,Digits-1])).
where Digits tell you the digit number.

Related

Overflow of an int in f#

I am working on some homework and we are supposed to be making a combination function in F#. I have got the factorial function down, but it seems to overflow once I get a big number to use factorial on. (Let's say 20) I understand I can use an int64 or a float, but that would change all the inputs on the code. What data type should I use?
let rec Fact (a:int)=
if (a = 0) then 1 else a*Fact(a-1);;
let combo (n:int) (k:int)=
if (n = 0) then 0 else (Fact n)/((Fact k)*(Fact (n-k)));;
On the code right now, when I do combo 20 5;; it gives me 2147. Which is clearly the wrong answer. I looked at the factorial function and when I put 20 in there it gave me a big negative number. Any help would be much appreciated. Thanks in advance.
First of all, if you want to avoid surprises, you can open the Checked module at the top of your file. This will redefine the numerical operators so that they perform overflow checks - and you'll get an exception rather than unexpected number:
open Microsoft.FSharp.Core.Operators.Checked
As Fyodor points out in the comment, you cannot fit factorial of 20 in int and you need int64. However, your combo function then performs division which will make the result of combo 20 5 small enough to fit into int.
One option is to change Fact to use int64, but keep combo as a function that takes and returns integers - you'll need to convert them to int64 before calling Fact and then back to int after performing the division:
let rec Fact (a:int64) =
if (a = 0L) then 1L else a * Fact(a-1L)
let combo (n:int) (k:int) =
if (n = 0) then 0 else int (Fact (int64 n) / (Fact (int64 k) * Fact (int64 (n-k))))
Now you can call combo 20 5 and you'll get 15504 as the result.
EDIT: As noted by #pswg in the other answer, int64 is also quite limited and so you'll need BigInteger for larger factorials. However, the same method should work for you with BigInteger. You can keep the combo function as a function that returns int by converting back from BigInteger to int.
You simply won't be able to do that with an 32-bit integer (int). A 64-bit integer will get you up to 20!, but will fail at 21!. The numbers just get too big, too quickly. To go any further than that you'll need to use System.Numerics.BigInteger (abbreviated bigint in F#).
The parameter can probably stay as an int to be reasonable, but you need to return a bigint:
let rec Fact (n : int) =
if n = 0 then bigint.One else (bigint n) * Fact (n - 1)
Or to be a little more idiomatic:
let rec Fact = function | 0 -> bigint.One | n -> (bigint n) * Fact (n - 1)
And now, in your Combo function, you'll need to use these bigint's internally for all math (thankfully the integer division is all you need in this case).
let Combo (n : int) (k : int) =
if n = 0 then bigint.Zero else (Fact n) / ((Fact k) * (Fact (n - k)))
If you really wanted to make Combo return an int, you can do that conversion here:
let Combo (n : int) (k : int) =
if n = 0 then 0 else (Fact n) / ((Fact k) * (Fact (n - k))) |> int
Examples:
Combo 20 5 // --> 15504
Combo 99 5 // --> 71523144 (would break if you used int64)
Edit: By rethinking your implementation of Combo you can get some big performance improvements out of this. See this question on Math.SE for the basis of this implementation:
let ComboFast (n : int) (k : int) =
let rec Combo_r (n : int) = function
| 0 -> bigint.One
| k -> (bigint n) * (Combo_r (n - 1) (k - 1)) / (bigint k)
Combo_r n (if (2 * k) > n then n - k else k)
A quick benchmark showed this to be significantly faster than the Fact-based version above:
Function Avg. Time (ms)
Combo 99 5 30.12570
ComboFast 99 5 0.72364

How to convert a list of binary to char?

I would like to convert a list of zeros and ones to a char.
example :
bitToChar([1,0,0,0,1,0,1]) = $Q
Thanks.
Another way to do it is to use a bit string comprehension:
X = [1,0,0,0,1,0,1],
<<C:7>> = << <<Bit:1>> || Bit <- lists:reverse(X) >>,
$Q == C.
That is, pick one element at a time from the list, and use each element as a bit in the binary being built, and finally extract a seven-bit number into the variable C.
You can add $0 to each of them (to make it a string with $0s and $1s), reverse the list, and use list_to_integer/2 with base 2:
1> list_to_integer(lists:reverse([N + $0 || N <- [1,0,0,0,1,0,1]]), 2) == $Q.
true
You can also use lists:foldl. The code is slightly longer but it doesn't use list_to_binary:
1> element(2, lists:foldl(fun(Digit, {Mul, Acc}) -> {Mul * 2, Acc + Digit * Mul} end, {1, 0}, Xs)) == $Q. true
This is basically equivalent to doing: 1 * 1 + 0 * 2 + 0 * 4 + 0 * 8 + 1 * 16 + 0 * 32 + 1 * 64.
$Q = lists:foldr(fun(X,Acc) -> X + (Acc bsl 1) end, 0,[1,0,0,0,1,0,1]).
Since $Q is integer value all you have to do is use in BitToChar conversion from binary based number to decimal based number.
Simplest conversion is:
to_decimal(X) ->
to_decimal(lists:reverse(X), 1, 0).
% you can validate that if H = 1 then add, if other not but I omitted this validation
to_decimal([H | T], Times, Acc) ->
to_decimal(T, 2 * Times, H * Times + Acc);
to_decimal([], _Times, Acc) -> Acc.
And then it will return integer.
In your case:
> $Q = 81.
81
> $Q == 81.
true

How to get an random integer with limit length?

I want to create a function get_id(max_length). At first want to math:pow/2, but it return float data type. It seems not a good idea.
with code as follows, but only support max length=20, as it is hardcoded, any good idea?
seed()->
{M_a,M_b,M_c} = now(),
random:seed(M_a,M_b,M_c),
ok.
get_id(1)-> random:uniform(1);
get_id(2) -> random:uniform(10);
get_id(3) -> random:uniform(100);
get_id(4) -> random:uniform(1000);
get_id(5) -> random:uniform(10000);
get_id(6) -> random:uniform(100000);
get_id(7) -> random:uniform(1000000);
get_id(8) -> random:uniform(10000000);
get_id(9) -> random:uniform(100000000);
get_id(10) -> random:uniform(1000000000);
get_id(11) -> random:uniform(10000000000);
get_id(12) -> random:uniform(100000000000);
get_id(13) -> random:uniform(1000000000000);
get_id(14) -> random:uniform(10000000000000);
get_id(15) -> random:uniform(100000000000000);
get_id(16) -> random:uniform(1000000000000000);
get_id(17) -> random:uniform(10000000000000000);
get_id(18) -> random:uniform(100000000000000000);
get_id(19) -> random:uniform(1000000000000000000);
get_id(20) -> random:uniform(10000000000000000000).
Your approach, unfortunately, doesn't work. Indeed, while random:uniform/1 accepts any positive integer as its argument, it does not deliver a random integer uniformly distributed between 1 and N for very large values of N (in spite of what documentation claims).
The reason is that random:uniform/1 is actually truncating the product of its argument by the value of random:uniform/0 (and adding 1 for [1-N] range instead of [0-(N-1)]).
See source code: https://github.com/erlang/otp/blob/maint/lib/stdlib/src/random.erl#L112
And floats are IEEE 754 doubles with 53 bits mantissa, which means that get_id/1 will not return all possible values for input from 17 to 20 (with 16 or more digits).
random:uniform/0,1 is known as a poor random generator, mostly suitable if you want to generate reproductible pseudo-random series (a given seed value will always generate the same series). For this reason, I would suggest using crypto:rand_uniform/2.
A simple solution would be to compute 10^(N-1) using integer arithmetics (to avoid the 53 bits mantissa issue) and then call crypto:rand_uniform/2. You can perform this with a naive recursive implementation (pow1/1 below), or use binary exponentiation (pow2/1 below).
-define(BASE, 10).
-spec pow1(non_neg_integer()) -> pos_integer().
pow1(N) when N >= 0 ->
pow1(N, 1).
pow1(0, Acc) -> Acc;
pow1(N, Acc) ->
pow1(N - 1, Acc * ?BASE).
-spec pow2(non_neg_integer()) -> pos_integer().
pow2(N) when N >= 0 ->
pow2(?BASE, N, 1).
pow2(_X, 0, Acc) ->
Acc;
pow2(X, N, Acc) when N rem 2 =:= 0 ->
pow2(X * X, N div 2, Acc);
pow2(X, N, Acc) ->
pow2(X * X, N div 2, Acc * X).
Your function could simply be written as:
-spec get_id2(pos_integer()) -> non_neg_integer().
get_id2(N) ->
1 + crypto:rand_uniform(0, pow2(N - 1)).
Alternatively, you could use a combination of uniform random variables, one per digit (while the sum of two random uniform variables is generally not a uniform random variable, it is if combined like this) or for several digits in the case of the binary exponentiation.
With the naive exponentiation:
-spec get_id3(pos_integer()) -> pos_integer().
get_id3(N) when N > 0 ->
get_id3(N - 1, 0).
get_id3(0, Acc) -> 1 + Acc;
get_id3(N, Acc) ->
Acc1 = crypto:rand_uniform(0, ?BASE) + (Acc * ?BASE),
get_id3(N - 1, Acc1).
With the binary exponentiation:
-spec get_id4(pos_integer()) -> pos_integer().
get_id4(N) when N > 0 ->
get_id4(?BASE, N - 1, 0).
get_id4(_X, 0, Acc) ->
1 + Acc;
get_id4(X, N, Acc) when N rem 2 =:= 0 ->
get_id4(X * X, N div 2, Acc);
get_id4(X, N, Acc) ->
Acc1 = crypto:rand_uniform(0, X) + (Acc * X),
get_id4(X * X, N div 2, Acc1).
Why not use trunc/1 to cast the floats returned by math:pow/2 to integers? http://www.erlang.org/doc/man/erlang.html#trunc-1
like in any language, you can get a power of 2 by shifting left the number 1:
1> 1 bsl 3.
8
2> 1 bsl 8.
256
3> 1 bsl 852.
30030067315218800919884630782037027445247038374198014146711597563050526250476926831789640794321325523394216076738821850476730762665208973047045843626559620640158907690363610309346513399556581649279919071671610504617321356178738468477058455548958390664298496
4>
As you can see, the size of integer is not limited in erlang. It is both good and bad since small integer (that is integer represented as a single worg like in most languages) are limited depending on the architecture:
On 32-bit architectures: -134217729 < i < 134217728 (28 bits)
On 64-bit architectures: -576460752303423489 < i < 576460752303423488 (60 bits)
for bigger integer, another representation is used: big integer, that takes more space in memory and take longer to compute.

Erlang: Faster way to sum bytes in a binary

Below is simple implemenation that adds bytes of a binary. It is slow according to eprof (takes about 10% of total time - mostly because of many calls to binary:part/3).
How can this be optimzed ?
calc_checksum(Packet) when is_binary(Packet)->
calc_checksum(Packet, 0).
calc_checksum(<<>>, Acc) ->
Acc band 16#FFFF;
calc_checksum(Packet, Acc) when is_binary(Packet) ->
W = binary:decode_unsigned(binary:part(Packet, 0, 2), little),
NextAcc = Acc + W,
NextBytes = binary:part(Packet, byte_size(Packet), -(byte_size(Packet)-2)),
calc_checksum(NextBytes, NextAcc).
A more elegant solution is:
calc_checksum(<<W:16/little,Rest/bytes>>, Acc0) ->
Acc1 = Acc0 + W,
calc_checksum(Rest, Acc1);
calc_checksum(<<>>, Acc) -> Acc band 16#FFFF.
This code will generate an error if the binary contains an odd number of bytes. Using pattern matching usually gives better more elegant code.
Using pattern matching instead of calling functions in binary seems to double the speed in the pseudo-benchmark I tried in the shell. Something like this:
calc_checksum(Packet, Acc) when is_binary(Packet) ->
<<W:16/little, NextBytes/binary>> = Packet,
NextAcc = Acc + W,
calc_checksum(NextBytes, NextAcc).
(I might be wrong, but you should get equivalent results if you set NextAcc to (Acc + W) band 16#FFFF, which should avoid bignums if you run this on really big binaries.)
If you process several values in once you can even speed up little bit more than Robert's solution:
calc_checksum(<<W1:16/little, W2:16/little, W3:16/little, W4:16/little, Rest/bytes>>, Acc)->
calc_checksum(Rest, Acc+W1+W2+W3+W4);
calc_checksum(<<W:16/little,Rest/bytes>>, Acc) ->
calc_checksum(Rest, Acc+W);
calc_checksum(<<>>, Acc) -> Acc band 16#FFFF.

hex to 64 Signed Decimal

i have hexdecimal data i have to convert into 64 Signed Decimal data ..so i thought have follwoing step like this.
1.hexadecimal to binary,
instead of writing my own code conversion i m using code given in this link http://necrobious.blogspot.com/2008/03/binary-to-hex-string-back-to-binary-in.html
bin_to_hexstr(Bin) ->
lists:flatten([io_lib:format("~2.16.0B", [X]) ||
X <- binary_to_list(Bin)]).
hexstr_to_bin(S) ->
hexstr_to_bin(S, []).
hexstr_to_bin([], Acc) ->
list_to_binary(lists:reverse(Acc));
hexstr_to_bin([X,Y|T], Acc) ->
{ok, [V], []} = io_lib:fread("~16u", [X,Y]),
hexstr_to_bin(T, [V | Acc]).
2.binary to decimal,
how to achieve this part.?
or any other way to achieve the hexdecimal -> 64 Signed Decimal data
thanx in advance
To convert an integer to a hex string, just use erlang:integer_to_list(Int, 16). To convert back, use erlang:list_to_integer(List, 16). These functions take a radix from 2 to 36 I believe.
If you want to convert binaries to and from hex strings you can use list comprehensions to make it tidier:
bin_to_hex(Bin) -> [ hd(erlang:integer_to_list(I, 16)) || << I:4 >> <= Bin ].
hex_to_bin(Str) -> << << (erlang:list_to_integer([H], 16)):4 >> || H <- Str >>.
To convert an integer to a hex string containing a 64 bit signed integer, you can now do:
Int = 1 bsl 48, HexStr = bin_to_hex(<<Int:64/signed-integer>>),
Bin = hex_to_bin(HexStr), <<RoundTrippedInt:64/signed-integer>> = Bin,
Int =:= RoundTrippedInt.
What about this approach?
hex2int(L) ->
<< I:64/signed-integer >> = hex_to_bin(L),
I.
int2hex(I) -> [ i2h(X) || <<X:4>> <= <<I:64/signed-integer>> ].
hex_to_bin(L) -> << <<(h2i(X)):4>> || X<-L >>.
h2i(X) ->
case X band 64 of
64 -> X band 7 + 9;
_ -> X band 15
end.
i2h(X) when X > 9 -> $a + X - 10;
i2h(X) -> $0 + X.

Resources