3

so currently I'm helping develop a programming language, and we've reached the point where we have to implement a fixed point type using C++ as our backbone language to write this in. I am able to figure out how to add, subtract, multiply, divide, however I am drawing a blank for how to accomplish this for modulus and exponentials. I feel I am close to figuring out exponentials, so I will focus this question on modulus.

Currently what I do is I take in a string literal and track the point of the radix and find the distance of that from the end of the string, and this gives me my scaling factor. After that we keep the entire big integer, which is supposed to be constrained to a fixed type (unless circumstances like division...or possibly modulus, arise, at which point we increase the fractional side of the radix to atleast half of the size of the integer portion). I have implemented ways to grab values to the left of the radix and to the right of the radix by multiplying by factors of 10 (I want the accuracy that comes with base 10 as opposed to the speed of base 2) to get the place where the radix will sit. I have googled looking up how to implement modulus on fixed point types, to no avail.

I can't seem to figure out how one would go about implementing it. Is there a way of implementing this? Do people know of a generalized algorithm? Anything to point me in the right direction? Remember, I'm aiming for accuracy. Speed is nice too, but the prime directive is accuracy.

To clarify, the hardware we would be operating on is generalized. As for the definition of what I want...that's somewhat why I'm here. I don't know what I want, I want to know some examples and different options to choose from. Really I'm just trying to learn about this.

EXAMPLE:

say I have a fixed8x8 and I push out 2 % 1.2 (2 could be 2.0 as well here), the result should come back 0.8. What's a good rule for going about extending the size of the right side to compensate for accuracy?

  • What does "modulus" mean in this context? Fixed-point numbers represent real numbers, and over the real numbers multiplication is invertible, so there is no concept of "remainder". – Kerrek SB Feb 25 '16 at 20:25
  • 1
    "I want the accuracy that comes with base 10 as opposed to the speed of base 2" huh? you expect to get more accuracy by using base 10? – 463035818_is_not_an_ai Feb 25 '16 at 20:27
  • I'm not sure I understand what you mean by multiplication being invertible. I get that they represent real numbers, but surely there is still a way to grab a remainder out of the bunch. Are you telling me that all the fraction calculation on modulus are done in floating point and perhaps I misunderstand? – Richard John Catalano Feb 25 '16 at 20:28
  • @tobi303 according to this wikipedia page on fixed point arithmetic, a base 10 leads to more human readability and increased accuracy...unless I'm misinterpreting it. https://en.wikipedia.org/wiki/Fixed-point_arithmetic#Operations – Richard John Catalano Feb 25 '16 at 20:28
  • the remainder of 1.2/0.5 is zero. please provide an example, otherwise it is not clear what you mean – 463035818_is_not_an_ai Feb 25 '16 at 20:29
  • @KerrekSB It probably means the least non-negative integral value of `n` such that `m*y + n = x` for some integer `m` and the inputs `x` and `y`. Same as it does for integers, and same as `fmod` does. – user253751 Feb 25 '16 at 20:30
  • 1
    @tobi303 let's assume that the fixed point can expand to a larger size automatically...It's my first time going through this and this is somewhat difficult to grasp for me, hence why I am seeking help. So I would think that in your case, that actually, the remainder would be 0.2...would it not? – Richard John Catalano Feb 25 '16 at 20:35
  • @immibis I'm actually not certain of that definition. While I am using real numbers, I also am working with signed and unsigned versions. – Richard John Catalano Feb 25 '16 at 20:42
  • @RichardJohnCatalano I think that you need to read up on what the modulus operation is. Modulus should always yield a whole number. – Jacobr365 Feb 25 '16 at 20:42
  • 1
    i might be wrong, but i think you are mislead by the wikipedia article that uses base 10 numbers for the examples. `int` and `float` are stored as base 2, and there is no difference in precision. The only loss in precision is when you store base 10 numbers that we use in real life as base 2 numbers of limited precision. – 463035818_is_not_an_ai Feb 25 '16 at 20:43
  • 1
    @Jacobr365 then why does Wolfram Alpha and Google disagree? I'm not trying to be conflictive here, I'm just saying that I can plug in a modulus calculation into them and they seem to pop out the remainder...is there something I'm fundamentally misunderstanding here? – Richard John Catalano Feb 25 '16 at 20:44
  • @tobi303 hmmm....but is that a fair comparison? I'm not trying to make a floating point number, I actually want to avoid it. I want only a fixed point number. – Richard John Catalano Feb 25 '16 at 20:45
  • @tobi303 The remainder of 1.2/0.5 is 0.2, because 1.2 - (2*0.5) = 0.2, and 2 is the greatest integer n such that n*0.5 <= 1.2 – user253751 Feb 25 '16 at 20:46
  • @RichardJohnCatalano As I understand it your definition of modulus of two floating point numbers a,b mod(a, b) = c if a = b*k + c where k is integer and c is float? – Pratik Deoghare Feb 25 '16 at 20:50
  • 1
    [wiki](https://en.wikipedia.org/wiki/Modulo_operation): "[...]thus their definition of the modulo operation depends on the programming language and/or the underlying hardware." I think you should put a definition (or refer to one) in your question – 463035818_is_not_an_ai Feb 25 '16 at 20:53
  • @tobi303 so the programming language I'm working on is Solidity per Ethereum (see here: http://solidity.readthedocs.org/en/latest/ ), so we're in the process of defining that. I guess I don't know what my options are and am trying to learn more. The hardware is generalized. Anything that you or I can operate on. – Richard John Catalano Feb 25 '16 at 20:59
  • @PratikDeoghare no, not floating point numbers. They are real numbers. Think of them as big dynamic integers from which all calculations span. There is no floating point operation performed on them (too intensive CPU wise for what we're doing). Your calculation may have some merit (I'm not sure, I'm trying to learn) but I want to make sure you understand the problem. – Richard John Catalano Feb 25 '16 at 21:02
  • 1
    @immibis: That's one possible interpretation, but it is somewhat arbitrary. Over the integers, modular arithmetic arises naturally from the subgroup of "multples of n", but when multiplication is invertible, *any* number is multiple of n. Picking out the integers from the reals is a bit arbitrary. – Kerrek SB Feb 25 '16 at 21:13
  • @tobi303 I don't understand how it is esoteric at all. Fixed point may not be commonly used, but it is utilized, and it has benefits when up against a floating point number. As for the FAQ...yes. We did. Because for the most part it's a functioning language. I'm currently implementing the fixed point arithmetics. The assembler has been built via a virtual machine and I am simply trying to create a proper type for the language that can handle the modulus operator for both integers and fixed point numbers. – Richard John Catalano Feb 25 '16 at 21:23
  • @KerrekSB it may be a bit arbitrary, sure. So I guess my question is, how might one go about it? What are some different ways? I'm looking for ideas. – Richard John Catalano Feb 25 '16 at 21:25
  • 1
    Using your example of 1.2 % 2 == 0.8, what should 1.2 / 2 be? Is it 1.2, or 0, or something else? I implemented a 16x8 bit fixed point library for the PIC 16F series of micro-controllers, so I have a bit of experience with fixed point, but the benefit of fixed-point is to simplify real number representations so that you can compute 1.2 /2 = 0.6 without the overhead and complexity of floating point. What is the purpose behind a modulus operation, and how would you expect to use it? – Matt Jordan Feb 25 '16 at 21:26
  • @MattJordan 0.6 for the division question. The purpose of the modulus operation is simply to enable it for the use of developers. Currently we are creating a type for literals to be pushed into either a fixed type or an integer type, and the ability to calculate fluidly between them is something of a desire. Good question though regarding the use case. I would imagine something along the lines of an intensive math library or large calculations utilizing scoring systems in a distributed AI platform. Really the possibilities are unlimited. I'm just helping build the tools for others to use. – Richard John Catalano Feb 25 '16 at 21:34
  • @MattJordan also, like you said, we are really trying to keep away from the complexity and overhead of a floating point. Matter of fact it's imperative. – Richard John Catalano Feb 25 '16 at 21:35
  • @MattJordan I would really, REALLY appreciate anything you could offer in terms of mathematic knowledge, computational knowledge, anything on the matter. I'm simply trying to understand this better so I can go and implement it for this language. Any and all possible ways from your experience are welcome and feel free to leave them in an answer. – Richard John Catalano Feb 25 '16 at 21:37
  • 1
    So, it sounds like the use-case will mostly be for integers that are moved from integer to fixed-point variables in preparation for other calculations, so can you simply define the result of modulus as the integer equivalent, and ignore the decimal portion, or were you given the specification for 1.2 % 2 == 0.8 ? – Matt Jordan Feb 25 '16 at 21:41
  • 1
    1.2 % 2 should be 1.2; I think @RichardJohnCatalano made a mistake in that example. – user253751 Feb 25 '16 at 21:42
  • @MattJordan precisely. I was just using that as an example, but it's a bit of both going on here. I would like it to be able to handle in preparation for other calculations, but I would also like to be able to handle example cases like the one above. Is there some kind of tradeoff between the two? Will there be some kind of rounding needing to be defined? – Richard John Catalano Feb 25 '16 at 21:45
  • @immibis yup, made a mistake there. Let me flip them. Thanks for pointing that out. – Richard John Catalano Feb 25 '16 at 21:45
  • 1
    @RichardJohnCatalano Seems to me that you could implement it with integer modulo, with the precision being the greatest of the left and right sides' precisions. – user253751 Feb 25 '16 at 21:51
  • @immibis can you elaborate on your thinking? Please, feel free to give it in the answer. – Richard John Catalano Feb 25 '16 at 21:56

1 Answers1

4

With the correction of 1.2 % 2 == 1.2, then just ignore the decimal, e.g. this is the same as 12 % 20 == 12

You can just use subtraction to compute this, if speed is truly not necessary. E.g. for any A % M, just keep subtracting M from A until A < M, for M != 0 (infinite loop!). This will provide the same result, 1.2, since subtraction really isn't affected by decimal points when represented as fixed-point.

If speed matters, and you don't have multiplication routines that can ignore the decimal (most won't), you may need to build them, since at 8x8 you won't have enough space to shift the entire value into the left-side (for some operations, I implemented 24x8 fixed point registers in my PIC 16F library to allow higher precision when performing computations on 16x8 fixed point).

To elaborate on using multiplication for faster arithmetic, and the reason you would need multiplication routines that can ignore the decimal,

First we analyze the slower subtraction approach using this pseudo-C++ code:

auto answer = A;
while(answer >= M)
  answer -= M;
// now, your answer is in answer

But if we knew how many iterations were necessary beforehand, we could just:

auto answer = A - M * number_of_iterations;

Of course, we won't know that, so the goal is to reduce the number of iterations that we need rather. This can be done with reverse-factorization - given a number M, find how many times you can multiply it by 10 (one digit to the left) and it still be less than A, so e.g. if A is 1023.32 and M is 4.1, you can multiply M by 10 twice, resulting in M*10*10 = 410. Subtract this from A until A < M*10*10, leaving A'=203.32, and continue to repeat until your working copy of A is less than M. This turns approach generally turns an O(N) operation into an O(log(N)) operation, provided you don't add too much overhead with the computations (in my PIC 16F implementation, I had to be careful because the chip only had 384 bytes of total memory, so needless to say, it was slower than it could have been).

I mostly did my work in base-2, but the same ideas translate to base-10 as well, so something like this should greatly reduce the number of iterations, especially for larger items (and this can be adjusted depending on your internal representation, so you can use digit-shifting rather than multiplication for each step):

auto currentA = A;
while(currentA >= M){
  auto scaledM = M;
  while(scaledM*10 < currentA)
    scaledM *= 10;
  // this second loop prevents recomputing scaledM where
  // currentA > n*scaledM for some n < 10; not needed in base-2
  while(currentA > scaledM)
    currentA -= scaledM;
}

And currentA will contain your modulus when this completes.

Matt Jordan
  • 2,133
  • 9
  • 10
  • I'm going to digest this a while and then come to you with a couple of questions. – Richard John Catalano Feb 25 '16 at 22:04
  • Do you think you can elaborate on building the multiplication routines? Something of an example like the formula above? – Richard John Catalano Feb 25 '16 at 22:06
  • Yep. That wins it. I'm going to keep coming back here, possibly with more questions. Thanks so much for the help. – Richard John Catalano Feb 25 '16 at 22:44
  • Okay, I had to try it with a calculator, using "integers" and keeping track of the decimal points myself. In fact, it does work, provided that `A` and `B` in `A % B` are the same fixed-point format. And the answer is automatically in that format as well. +1 from me. – AaronD Jun 17 '17 at 00:41