DEV Community

Cover image for Deterministic Base Conversion with Logarithms
Anthony
Anthony

Posted on

Deterministic Base Conversion with Logarithms

Intro

This article walks through several solutions for a decimal to octal number convertor coding challenge written in ruby.
Note: These solutions can be generalized for converting decimal to any base less than 10 by adding a second function parameter and replacing all 8's with the given parameter.

Solution 1: Standard Way

One simple way to convert bases is to repeatedly take the modulo of the input number and target base num % 8 then divide the input number by target basenum /= 8 until the number is empty while num > 0. Each modulo result is either added to an array or summed into a resultant number like in the example below.

def octal_convertor(num)
  octal = 0
  i = 1
  while num > 0
    octal += (num % 8) * i 
    num /= 8
    i *= 10
  end
  return octal
end
Enter fullscreen mode Exit fullscreen mode

This solution uses a variable i = 1 which grows by i *= 10 on each iteration. It is used to multiply num % 8 so it goes in the next whole number place in octal += (num % 8) * i. You can think of it like slicing off base 8 digits from num and slotting them into the next whole number place in octal

Solution 2: Deterministic Iteration with Logarithm

The number of places(digits) in a whole number of any base(base 8 in this instance) can be calculated with Math.log(num,8).floor + 1. This reveals how many digits need to be 'sliced' out with num % 8, or rather the number of loop iterations required. So there is no need to check if num > 0, we can just run a loop an exact number of times.

def octal_convertor(num)
   octal = 0
   (Math.log(num,8).floor + 1).times do |i|
     octal += (num % 8) * 10**i
     num /= 8
   end
   return octal
end
Enter fullscreen mode Exit fullscreen mode

The .times method accepts a block and passes iteration count to |i|. So in addition to knowing the exact number of iterations in advance the 'i' variable in the previous solutions is provided by .times. The |i| increments from zero, so to slot the octal digits in the right place (num % 8) is multiplied by 10**i.

Solution 3: Number as an Array

This one-liner works much the same way as above but creates a new array with a length of the octal number (so there is a place for each oct digit). Similar to .times Array.new accepts a block where you can fill each value in the array with something, this block receives an index value |i|.

def octal_convertor(num)
  Array.new(Math.log(num,8).floor + 1) {|i| ((num / 8**i) % 8) * 10**i }.sum
end
Enter fullscreen mode Exit fullscreen mode

num is not mutably divided with num /= 8, instead, using 'i' we calculate how many times it should have been divided num / 8**i or rather which octal place value the current iteration is at. 10**i works exactly the same as previously mentioned. The array now contains all oct digits multiplied by 10**i, which provides the correct number of zeros added so when summed together they fit in the right place. .sum adds all numbers in the array together returning the converted number.

Conclusion

Calculating number digit lengths using logarithms provides a means to determine iteration count ahead of time and iterate over enumerable objects(arrays, numbers) instead of using while loops.

Discussion (0)